const ::internal::tflite::operand::Index activation_index{node.param().activation_index};
+ assert(_ctx.at(input_index).shape().rank() >= 2);
assert(_ctx.at(output_index).shape().rank() == 2);
- const auto output_size = _ctx.at(output_index).shape().dim(1);
- // NOTE We assume that input is a feature map
- // TODO Remove this restriction!
- const auto ifm_shape = _ctx.at(input_index).shape().asFeature();
+ auto no_of_input_elements = 1;
+ for (size_t i = 0; i < _ctx.at(input_index).shape().rank(); i++)
+ {
+ no_of_input_elements *= _ctx.at(input_index).shape().dim(i);
+ }
+ const auto output_size = _ctx.at(output_index).shape().dim(1);
assert(_ctx.at(weight_index).shape().rank() == 2);
const auto num_output = _ctx.at(weight_index).shape().dim(0);
const auto input_size = _ctx.at(weight_index).shape().dim(1);
- assert(ifm_shape.C * ifm_shape.H * ifm_shape.W == input_size);
+ uint32_t C, N, H, W;
+ if (_ctx.at(input_index).shape().rank() == 2)
+ {
+ nnfw::util::matrix::Shape ifm_shape_matrix;
+ ifm_shape_matrix = _ctx.at(input_index).shape().asMatrix();
+ H = ifm_shape_matrix.H;
+ W = ifm_shape_matrix.W;
+ N = num_output;
+ C = 1;
+ _builder.addShapeConstr(input_index, asTensorInfo(ifm_shape_matrix, _ctx.at(input_index).type(),
+ _ctx.at(input_index).scale(),
+ _ctx.at(input_index).zeroPoint()));
+ }
+ else
+ {
+ nnfw::util::feature::Shape ifm_shape_feature;
+ ifm_shape_feature = _ctx.at(input_index).shape().asFeature();
+ H = ifm_shape_feature.H;
+ W = ifm_shape_feature.W;
+ N = num_output;
+ C = ifm_shape_feature.C;
+ assert(C * H * W == input_size);
+ _builder.addShapeConstr(
+ input_index, asTensorInfo(ifm_shape_feature, _ctx.at(input_index).type(),
+ _ctx.at(input_index).scale(), _ctx.at(input_index).zeroPoint()));
+ }
+
+ const auto batches = no_of_input_elements / input_size;
const auto bias_size = _ctx.at(bias_index).shape().asVector();
// TODO Should move to the place where the operand is handled, if it is possible.
// Set Shape Constraints
- _builder.addShapeConstr(output_index, asTensorInfo(output_size, _ctx.at(output_index).type(),
- _ctx.at(output_index).scale(),
- _ctx.at(output_index).zeroPoint()));
- _builder.addShapeConstr(input_index, asTensorInfo(ifm_shape, _ctx.at(input_index).type(),
- _ctx.at(input_index).scale(),
- _ctx.at(input_index).zeroPoint()));
+ _builder.addShapeConstr(
+ output_index, asTensorInfo(batches, num_output, _ctx.at(output_index).type(),
+ _ctx.at(output_index).scale(), _ctx.at(output_index).zeroPoint()));
_builder.addShapeConstr(
weight_index, asTensorInfo(num_output /*H*/, input_size /*W*/, _ctx.at(weight_index).type(),
_ctx.at(weight_index).scale(), _ctx.at(weight_index).zeroPoint()));
{
case ANEURALNETWORKS_TENSOR_FLOAT32:
{
- auto initializer = [num_output, ifm_shape, weight_base,
+ auto initializer = [num_output, N, C, H, W, weight_base,
weight_size](::arm_compute::ITensor &tensor) {
- const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H,
- ifm_shape.W};
+ const ::nnfw::util::kernel::Shape ker_shape{N, C, H, W};
const ::internal::nnapi::kernel::Reader<float> from{ker_shape, weight_base, weight_size};
::nnfw::util::kernel::iterate(ker_shape)
<< [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
uint32_t offset = 0;
// ARM Compute Library uses 'NCHW' ordering
- offset += nth * ifm_shape.C * ifm_shape.H * ifm_shape.W;
- offset += ch * ifm_shape.H * ifm_shape.W;
- offset += row * ifm_shape.W;
+ offset += nth * C * H * W;
+ offset += ch * H * W;
+ offset += row * W;
offset += col;
const ::arm_compute::Coordinates coordinate{offset};
}
case ANEURALNETWORKS_TENSOR_QUANT8_ASYMM:
{
- auto initializer = [num_output, ifm_shape, weight_base,
+ auto initializer = [num_output, N, C, H, W, weight_base,
weight_size](::arm_compute::ITensor &tensor) {
- const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H,
- ifm_shape.W};
+ const ::nnfw::util::kernel::Shape ker_shape{N, C, H, W};
+
const ::internal::nnapi::kernel::Reader<uint8_t> from{ker_shape, weight_base,
weight_size};
::nnfw::util::kernel::iterate(ker_shape)
uint32_t offset = 0;
// ARM Compute Library uses 'NCHW' ordering
- offset += nth * ifm_shape.C * ifm_shape.H * ifm_shape.W;
- offset += ch * ifm_shape.H * ifm_shape.W;
- offset += row * ifm_shape.W;
+ offset += nth * C * H * W;
+ offset += ch * H * W;
+ offset += row * W;
offset += col;
const ::arm_compute::Coordinates coordinate{offset};