Commit 7f6f07ee authored by Adam Procter's avatar Adam Procter Committed by Scott Cyphers

Partial Shapes and Types, Part 4j: Quantize/Dequantize (#1842)

* Adapt Tensor class to have partial shapes

* Add PartialShapes to Input, Output, Function, Node classes

* Terminological cleanup

* Add PartialShape propagation for Parameter and Result

* Implement partial-shape propagation for elementwise ops

* More comments

* One more comment tweak

* Add tests for the merge functions

* Add merging of undetermined element types

* Fix a goophup in deserializer implementation

* Implement fallback for ops that do not support partial shape/type validation

* Updates for some older unit tests, now that operator[] exists

* Add missing validate_punt_if_incomplete to AllReduce

* Implement partial shape/type propagation for AllReduce

* Implement partial shape/type propagation for Reshape

* Remove unneeded validate_punt from Result

* Implement partial shape/type propagation for Reverse

* Implement partial shape/type validation for ReverseSequence

* Implement partial shape/type validation for ArithmeticReduction

* Better docstrings for the stuff introduced in #1692; remove prototype for unimplemented, unused PartialShape::append()

* One more docstring thing I forgot to save

* Switch terminology from 'determined/undetermined' to 'static/dynamic'

* Switch terminology from 'complete/incomplete' to 'static/dynamic' for shapes; fix up some mushily worded comments

* Fix overzealous edits from the last commit

* Rename one test that escaped the Great Renaming

* Remove unnecessary validate_punt_if_dynamic from Reshape

* Fix comment typo

* Rewrite operator+ and operator* for Dimension as members, not friends

* Formatting tweak

* Show argument types/shapes in long NodeDescription; tank unit tests to block merge

* Fix dynamic element type propagation for elementwise ops, add some unit tests for same

* Fix error message

* Roll 'Not' back to existing behavior (non-boolean input types allowed)

* Add a TODO tag to a todo item

* Add unit tests for partial shape/type propagation with ReverseSequence

* Add unit tests for partial-shape/type propagation for ArithmeticReduction (via Sum)

* Implement partial type/shape propagation for GetOutputElement

* Function signatures

* Add implementations, unit tests for relaxes/refines functions

* Generalize project/reduce/inject functions to cover PartialShape, move to shape_util.[ch]pp

* Dynamic shpae/type prop for Quantize

* Add unit tests for partial shape/type validation for Quantize

* Implement partial shape/type validation for Dequantize, with unit tests

* Remove #if 0'd code

* Deal with std::find_if #include issues

* Fix more include madness
parent 925e7b27
......@@ -35,11 +35,6 @@ op::Dequantize::Dequantize(shared_ptr<Node> input,
void op::Dequantize::validate_and_infer_types()
{
if (validate_punt_if_dynamic())
{
return;
}
enum
{
INPUT,
......@@ -47,41 +42,82 @@ void op::Dequantize::validate_and_infer_types()
OFFSET
};
set_output_size(1);
set_output_type(0, m_type, get_input_shape(INPUT));
NODE_VALIDATION_ASSERT(this, get_input_element_type(INPUT).is_quantized())
<< "Input element type (" << get_input_element_type(INPUT) << ") must be a quantized type";
NODE_VALIDATION_ASSERT(this, m_type.is_static()) << "Output element type must not be dynamic";
NODE_VALIDATION_ASSERT(this, m_type.is_real()) << "Output element type (" << m_type
<< ") must be a floating point number";
<< ") must be a floating point type";
NODE_VALIDATION_ASSERT(this, get_input_element_type(SCALE) == m_type)
<< "Scale element type (" << get_input_element_type(SCALE)
<< ") must match the output element type (" << m_type << ")";
element::Type quantized_type;
NODE_VALIDATION_ASSERT(this, get_input_element_type(OFFSET) == get_input_element_type(INPUT))
NODE_VALIDATION_ASSERT(this,
element::Type::merge(quantized_type,
get_input_element_type(INPUT),
get_input_element_type(OFFSET)))
<< "Offset element type (" << get_input_element_type(OFFSET)
<< ") must match input element type (" << get_input_element_type(INPUT) << ")";
NODE_VALIDATION_ASSERT(this, quantized_type.is_dynamic() || quantized_type.is_quantized())
<< "Offset/input element type (" << quantized_type << ") must be a quantized type";
element::Type unquantized_type;
NODE_VALIDATION_ASSERT(
this, element::Type::merge(unquantized_type, get_input_element_type(SCALE), m_type))
<< "Scale element type (" << get_input_element_type(SCALE)
<< ") must match output element type (" << m_type << ")";
PartialShape input_shape = get_input_partial_shape(0);
Dimension input_rank = input_shape.rank();
for (auto axis : m_axes)
{
NODE_VALIDATION_ASSERT(this, axis < get_shape().size())
NODE_VALIDATION_ASSERT(this, input_rank.is_dynamic() || axis < size_t(input_rank))
<< "Quantization axis (" << axis << ") must be less than input shape rank ("
<< get_shape().size() << ")";
<< input_rank << ")";
}
Shape projected_shape = project(get_input_shape(INPUT), m_axes);
PartialShape scale_offset_shape = get_input_partial_shape(SCALE);
NODE_VALIDATION_ASSERT(
this, PartialShape::merge_into(scale_offset_shape, get_input_partial_shape(OFFSET)))
<< "Scale shape (" << get_input_partial_shape(SCALE) << ") and offset shape ("
<< get_input_partial_shape(OFFSET) << ") must match";
NODE_VALIDATION_ASSERT(this, get_input_shape(SCALE) == projected_shape)
<< "Scale shape (" << get_input_shape(SCALE)
<< ") must match input shape projected along the quantization axes (" << projected_shape
<< ")";
NODE_VALIDATION_ASSERT(this, scale_offset_shape.rank().compatible(m_axes.size()))
<< "Scale/offset rank (" << scale_offset_shape.rank() << ") does not match the number of "
<< "quantization axes (" << m_axes.size() << ")";
set_output_size(1);
NODE_VALIDATION_ASSERT(this, get_input_shape(OFFSET) == projected_shape)
<< "Offset shape (" << get_input_shape(OFFSET)
<< ") must match input shape projected along the quantization axes (" << projected_shape
<< ")";
if (input_shape.rank().is_static() && scale_offset_shape.rank().is_static())
{
size_t i = 0;
std::vector<Dimension> injected_scale_offset_dims;
for (size_t j = 0; j < size_t(input_shape.rank()); j++)
{
if (m_axes.count(j) != 0)
{
injected_scale_offset_dims.push_back(scale_offset_shape[i++]);
}
else
{
injected_scale_offset_dims.push_back(Dimension::dynamic());
}
}
PartialShape result_shape = input_shape;
NODE_VALIDATION_ASSERT(
this, PartialShape::merge_into(result_shape, PartialShape{injected_scale_offset_dims}))
<< "Scale/offset shape (" << scale_offset_shape << ") must match input shape ("
<< input_shape << ") at the quantization axes (" << m_axes << ")";
set_output_type(0, unquantized_type, result_shape);
}
else
{
set_output_type(0, unquantized_type, PartialShape::dynamic());
}
}
shared_ptr<Node> op::Dequantize::copy_with_new_args(const NodeVector& new_args) const
......
......@@ -37,11 +37,6 @@ op::Quantize::Quantize(shared_ptr<Node> input,
void op::Quantize::validate_and_infer_types()
{
if (validate_punt_if_dynamic())
{
return;
}
enum
{
INPUT,
......@@ -49,45 +44,85 @@ void op::Quantize::validate_and_infer_types()
OFFSET
};
set_output_size(1);
set_output_type(0, m_type, get_input_shape(INPUT));
NODE_VALIDATION_ASSERT(this, m_round_mode == RoundMode::HALF_AWAY_FROM_ZERO)
<< "Only RoundMode = HALF_AWAY_FROM_ZERO is supported, for now";
NODE_VALIDATION_ASSERT(this, m_type.is_static()) << "Output element type must not be dynamic";
NODE_VALIDATION_ASSERT(this, m_type.is_quantized()) << "Output element type (" << m_type
<< ") must be a quantized type";
NODE_VALIDATION_ASSERT(this, get_input_element_type(INPUT).is_real())
<< "Input element type (" << get_input_element_type(INPUT)
<< ") must be a floating point number";
element::Type unquantized_type;
NODE_VALIDATION_ASSERT(this, get_input_element_type(SCALE) == get_input_element_type(INPUT))
NODE_VALIDATION_ASSERT(this,
element::Type::merge(unquantized_type,
get_input_element_type(INPUT),
get_input_element_type(SCALE)))
<< "Scale element type (" << get_input_element_type(SCALE)
<< ") must match input element type (" << get_input_element_type(INPUT) << ")";
NODE_VALIDATION_ASSERT(this, get_input_element_type(OFFSET) == m_type)
NODE_VALIDATION_ASSERT(this, unquantized_type.is_dynamic() || unquantized_type.is_real())
<< "Scale/input element type (" << unquantized_type << ") must be a floating point number";
element::Type quantized_type;
NODE_VALIDATION_ASSERT(
this, element::Type::merge(quantized_type, get_input_element_type(OFFSET), m_type))
<< "Offset element type (" << get_input_element_type(OFFSET)
<< ") must match output element type (" << m_type << ")";
PartialShape input_shape = get_input_partial_shape(0);
Dimension input_rank = input_shape.rank();
for (auto axis : m_axes)
{
NODE_VALIDATION_ASSERT(this, axis < get_shape().size())
NODE_VALIDATION_ASSERT(this, input_rank.is_dynamic() || axis < size_t(input_rank))
<< "Quantization axis (" << axis << ") must be less than input shape rank ("
<< get_shape().size() << ")";
<< input_rank << ")";
}
Shape projected_shape = project(get_input_shape(INPUT), m_axes);
PartialShape scale_offset_shape = get_input_partial_shape(SCALE);
NODE_VALIDATION_ASSERT(this, get_input_shape(SCALE) == projected_shape)
<< "Scale shape (" << get_input_shape(SCALE)
<< ") must match input shape projected along the quantization axes (" << projected_shape
<< ")";
NODE_VALIDATION_ASSERT(
this, PartialShape::merge_into(scale_offset_shape, get_input_partial_shape(OFFSET)))
<< "Scale shape (" << get_input_partial_shape(SCALE) << ") and offset shape ("
<< get_input_partial_shape(OFFSET) << ") must match";
NODE_VALIDATION_ASSERT(this, get_input_shape(OFFSET) == projected_shape)
<< "Offset shape (" << get_input_shape(OFFSET)
<< ") must match input shape projected along the quantization axes (" << projected_shape
<< ")";
NODE_VALIDATION_ASSERT(this, scale_offset_shape.rank().compatible(m_axes.size()))
<< "Scale/offset rank (" << scale_offset_shape.rank() << ") does not match the number of "
<< "quantization axes (" << m_axes.size() << ")";
NODE_VALIDATION_ASSERT(this, m_round_mode == RoundMode::HALF_AWAY_FROM_ZERO)
<< "Only RoundMode = HALF_AWAY_FROM_ZERO is supported, for now";
set_output_size(1);
if (input_shape.rank().is_static() && scale_offset_shape.rank().is_static())
{
size_t i = 0;
std::vector<Dimension> injected_scale_offset_dims;
for (size_t j = 0; j < size_t(input_shape.rank()); j++)
{
if (m_axes.count(j) != 0)
{
injected_scale_offset_dims.push_back(scale_offset_shape[i++]);
}
else
{
injected_scale_offset_dims.push_back(Dimension::dynamic());
}
}
PartialShape result_shape = input_shape;
NODE_VALIDATION_ASSERT(
this, PartialShape::merge_into(result_shape, PartialShape{injected_scale_offset_dims}))
<< "Scale/offset shape (" << scale_offset_shape << ") must match input shape ("
<< input_shape << ") at the quantization axes (" << m_axes << ")";
set_output_type(0, quantized_type, result_shape);
}
else
{
set_output_type(0, quantized_type, PartialShape::dynamic());
}
}
shared_ptr<Node> op::Quantize::copy_with_new_args(const NodeVector& new_args) const
......
......@@ -8980,6 +8980,39 @@ TEST(type_prop, quantize_f64_to_u8_ok)
ASSERT_EQ(quant->get_output_shape(0), batch_shape);
}
TEST(type_prop, quantize_f64_to_dyn_fails)
{
Shape batch_shape{64, 3, 480, 640};
Shape scale_shape{};
Shape offset_shape{};
element::Type unquantized_type = element::f64;
element::Type quantized_type = element::dynamic;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Attempt to quantize to dynamic type not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(), "Output element type must not be dynamic");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(type_prop, quantize_i8_to_u8_fails)
{
Shape batch_shape{64, 3, 480, 640};
......@@ -9006,8 +9039,8 @@ TEST(type_prop, quantize_i8_to_u8_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Input element type (element::Type{8, 0, 1, 1, \"int8_t\"}) must be a "
"floating point number");
"Scale/input element type (element::Type{8, 0, 1, 1, \"int8_t\"}) "
"must be a floating point number");
}
catch (...)
{
......@@ -9180,8 +9213,7 @@ TEST(type_prop, quantize_scale_shape_mismatch_same_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape (Shape{64, 4}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,4}) and offset shape ({64,3}) must match");
}
catch (...)
{
......@@ -9215,8 +9247,7 @@ TEST(type_prop, quantize_scale_shape_mismatch_different_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape (Shape{64, 3, 2}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,3,2}) and offset shape ({64,3}) must match");
}
catch (...)
{
......@@ -9250,8 +9281,7 @@ TEST(type_prop, quantize_offset_shape_mismatch_same_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Offset shape (Shape{64, 4}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,3}) and offset shape ({64,4}) must match");
}
catch (...)
{
......@@ -9285,8 +9315,7 @@ TEST(type_prop, quantize_offset_shape_mismatch_different_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Offset shape (Shape{64, 3, 2}) must match input shape projected "
"along the quantization axes (Shape{64, 3})");
"Scale shape ({64,3}) and offset shape ({64,3,2}) must match");
}
catch (...)
{
......@@ -9315,7 +9344,7 @@ TEST(type_prop, quantize_offset_unsupported_round_mode_fails)
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Mismatch of offset argument shape with required shape not detected";
FAIL() << "Unsupported round mode not detected";
}
catch (const NodeValidationError& error)
{
......@@ -9328,6 +9357,281 @@ TEST(type_prop, quantize_offset_unsupported_round_mode_fails)
}
}
TEST(type_prop, quantize_partial_all_rank_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{PartialShape::dynamic()};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 2000};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
ASSERT_EQ(quant->get_output_element_type(0), quantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(type_prop,
quantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 2000};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
ASSERT_EQ(quant->get_output_element_type(0), quantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(
type_prop,
quantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_dynamic_axis_count_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Mismatch of scale/offset rank with axis count not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(
error.what(),
"Scale/offset rank (3) does not match the number of quantization axes (2)");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(type_prop,
quantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{64, 22, Dimension::dynamic(), Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
ASSERT_EQ(quant->get_output_element_type(0), quantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(
type_prop,
quantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ranks_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{64, 22, Dimension::dynamic(), Dimension::dynamic(), 3};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Inconsistent scale/offset ranks not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(
error.what(), "Scale shape ({64,?,96,?}) and offset shape ({64,22,?,?,3}) must match");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
quantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_dims_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{65, 22, Dimension::dynamic(), Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Inconsistent scale/offset dims not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape ({64,?,96,?}) and offset shape ({65,22,?,?}) must match");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
quantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ok)
{
PartialShape batch_shape{2, 4, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 5};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
ASSERT_EQ(quant->get_output_element_type(0), quantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).same_scheme(
PartialShape{2, 4, 6, 8, 10, Dimension::dynamic()}));
}
TEST(
type_prop,
quantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_axis_oob)
{
PartialShape batch_shape{2, 4, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 6};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Out-of-bound quantization axis not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Quantization axis (6) must be less than input shape rank (6)");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
quantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_dims_inconsistent)
{
PartialShape batch_shape{2, 5, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = unquantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 5};
auto round_mode = op::Quantize::RoundMode::HALF_AWAY_FROM_ZERO;
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant =
make_shared<op::Quantize>(batch, scale, offset, quantized_type, axes, round_mode);
FAIL() << "Inconsistent dimensions not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale/offset shape ({4,8,?}) must match input shape ({2,5,6,?,10,?}) "
"at the quantization axes (AxisSet{1, 3, 5})");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(type_prop, dequantize_f32_from_i8_nchw_per_channel_ok)
{
Shape batch_shape{64, 3, 480, 640};
......@@ -9500,7 +9804,7 @@ TEST(type_prop, dequantize_i8_from_u8_fails)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Output element type (element::Type{8, 0, 1, 1, \"int8_t\"}) must be "
"a floating point number");
"a floating point type");
}
catch (...)
{
......@@ -9531,9 +9835,9 @@ TEST(type_prop, dequantize_f32_from_f32_fails)
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(
error.what(),
"Input element type (element::Type{32, 1, 1, 0, \"float\"}) must be a quantized type");
EXPECT_HAS_SUBSTRING(error.what(),
"Offset/input element type (element::Type{32, 1, 1, 0, \"float\"}) "
"must be a quantized type");
}
catch (...)
{
......@@ -9599,8 +9903,9 @@ TEST(type_prop, dequantize_scale_type_mismatch_fails)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale element type (element::Type{64, 1, 1, 0, \"double\"}) must "
"match the output element type (element::Type{32, 1, 1, 0, "
"\"float\"})");
"match output element type (element::Type{32, 1, 1, 0, \"float\"})"
);
}
catch (...)
{
......@@ -9664,8 +9969,7 @@ TEST(type_prop, dequantize_scale_shape_mismatch_same_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape (Shape{64, 4}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,4}) and offset shape ({64,3}) must match");
}
catch (...)
{
......@@ -9697,8 +10001,7 @@ TEST(type_prop, dequantize_scale_shape_mismatch_different_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape (Shape{64, 3, 2}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,3,2}) and offset shape ({64,3}) must match");
}
catch (...)
{
......@@ -9730,8 +10033,7 @@ TEST(type_prop, dequantize_offset_shape_mismatch_same_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Offset shape (Shape{64, 4}) must match input shape projected along "
"the quantization axes (Shape{64, 3})");
"Scale shape ({64,3}) and offset shape ({64,4}) must match");
}
catch (...)
{
......@@ -9763,8 +10065,272 @@ TEST(type_prop, dequantize_offset_shape_mismatch_different_rank_fails)
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Offset shape (Shape{64, 3, 2}) must match input shape projected "
"along the quantization axes (Shape{64, 3})");
"Scale shape ({64,3}) and offset shape ({64,3,2}) must match");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
/////
/////
/////
TEST(type_prop, dequantize_partial_all_rank_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{PartialShape::dynamic()};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 2000};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
ASSERT_EQ(quant->get_output_element_type(0), unquantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(type_prop,
dequantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 2000};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
ASSERT_EQ(quant->get_output_element_type(0), unquantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(
type_prop,
dequantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_dynamic_axis_count_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96};
PartialShape offset_shape{PartialShape::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
FAIL() << "Mismatch of scale/offset rank with axis count not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(
error.what(),
"Scale/offset rank (3) does not match the number of quantization axes (2)");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(type_prop,
dequantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ok)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{64, 22, Dimension::dynamic(), Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
ASSERT_EQ(quant->get_output_element_type(0), unquantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).rank().is_dynamic());
}
TEST(
type_prop,
dequantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ranks_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{64, 22, Dimension::dynamic(), Dimension::dynamic(), 3};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
FAIL() << "Inconsistent scale/offset ranks not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(
error.what(), "Scale shape ({64,?,96,?}) and offset shape ({64,22,?,?,3}) must match");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
dequantize_partial_input_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_dims_inconsistent)
{
PartialShape batch_shape{PartialShape::dynamic()};
PartialShape scale_shape{64, Dimension::dynamic(), 96, Dimension::dynamic()};
PartialShape offset_shape{65, 22, Dimension::dynamic(), Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{0, 1, 5, 88};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
FAIL() << "Inconsistent scale/offset dims not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale shape ({64,?,96,?}) and offset shape ({65,22,?,?}) must match");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
dequantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_ok)
{
PartialShape batch_shape{2, 4, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 5};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
ASSERT_EQ(quant->get_output_element_type(0), unquantized_type);
ASSERT_TRUE(quant->get_output_partial_shape(0).same_scheme(
PartialShape{2, 4, 6, 8, 10, Dimension::dynamic()}));
}
TEST(
type_prop,
dequantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_axis_oob)
{
PartialShape batch_shape{2, 4, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 6};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
FAIL() << "Out-of-bound quantization axis not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Quantization axis (6) must be less than input shape rank (6)");
}
catch (...)
{
FAIL() << "Deduced type check failed for unexpected reason";
}
}
TEST(
type_prop,
dequantize_partial_input_static_rank_dynamic_scale_rank_static_dynamic_offset_rank_static_dynamic_dims_inconsistent)
{
PartialShape batch_shape{2, 5, 6, Dimension::dynamic(), 10, Dimension::dynamic()};
PartialShape scale_shape{4, Dimension::dynamic(), Dimension::dynamic()};
PartialShape offset_shape{Dimension::dynamic(), 8, Dimension::dynamic()};
element::Type unquantized_type = element::f32;
element::Type quantized_type = element::i8;
element::Type batch_type = quantized_type;
element::Type scale_type = unquantized_type;
element::Type offset_type = quantized_type;
AxisSet axes{1, 3, 5};
auto batch = make_shared<op::Parameter>(batch_type, batch_shape);
auto scale = make_shared<op::Parameter>(scale_type, scale_shape);
auto offset = make_shared<op::Parameter>(offset_type, offset_shape);
try
{
auto quant = make_shared<op::Dequantize>(batch, scale, offset, unquantized_type, axes);
FAIL() << "Inconsistent dimensions not detected";
}
catch (const NodeValidationError& error)
{
EXPECT_HAS_SUBSTRING(error.what(),
"Scale/offset shape ({4,8,?}) must match input shape ({2,5,6,?,10,?}) "
"at the quantization axes (AxisSet{1, 3, 5})");
}
catch (...)
{
......
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