Home | History | Annotate | Download | only in 1.0
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "android.hardware.neuralnetworks (at) 1.0-impl-hvx"
     18 
     19 #include "HexagonModel.h"
     20 #include <numeric>
     21 #include <unordered_set>
     22 #include "HexagonOperations.h"
     23 
     24 namespace android {
     25 namespace hardware {
     26 namespace neuralnetworks {
     27 namespace V1_0 {
     28 namespace implementation {
     29 namespace hexagon {
     30 
     31 static std::vector<OperandInfo> getOperandsInfo(const NeuralnetworksModel& model,
     32                                                 const std::vector<RunTimePoolInfo>& pools) {
     33     std::vector<OperandInfo> info(model.operands.size());
     34     for (size_t i = 0; i < model.operands.size(); ++i) {
     35         const Operand& operand = model.operands[i];
     36         info[i] = {
     37             .type = operand.type,
     38             .dimensions = operand.dimensions,
     39             .scale = operand.scale,
     40             .zeroPoint = operand.zeroPoint,
     41             .lifetime = operand.lifetime,
     42             .buffer = const_cast<uint8_t*>(getData(operand, model.operandValues, pools)),
     43             .length = operand.location.length,
     44         };
     45     }
     46     return info;
     47 }
     48 
     49 Model::Model(const NeuralnetworksModel& model) : mGraphId(0), mNodeCount(0), mCompiled(false) {
     50     mPools = mapPools(model.pools);
     51     mOperands = getOperandsInfo(model, mPools);
     52     std::for_each(mPools.begin(), mPools.end(), [](RunTimePoolInfo& mem) { mem.update(); });
     53 
     54     mOperations = model.operations;
     55     mInputs = model.inputIndexes;
     56     mOutputs = model.outputIndexes;
     57 }
     58 
     59 Model::Model(Model&& other) {
     60     *this = std::move(other);
     61 }
     62 
     63 Model& Model::operator=(Model&& other) {
     64     if (this != &other) {
     65         mNodeCount = other.mNodeCount;
     66         mGraphId = other.mGraphId;
     67         mCompiled = other.mCompiled;
     68         mOperands = std::move(other.mOperands);
     69         mOperations = std::move(other.mOperations);
     70         mInputs = std::move(other.mInputs);
     71         mOutputs = std::move(other.mOutputs);
     72         mPools = std::move(other.mPools);
     73         other.mNodeCount = 0;
     74         other.mGraphId = {};
     75         other.mCompiled = false;
     76     }
     77     return *this;
     78 }
     79 
     80 Model::~Model() {
     81     clearModel();
     82 }
     83 
     84 std::string Model::getLog() {
     85     char buffer[16 * 1024];
     86     int err = hexagon::Controller::getInstance().getlog(
     87         mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer));
     88     HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getLog");
     89     return buffer;
     90 }
     91 
     92 std::string Model::getGraph() {
     93     char buffer[16 * 1024];
     94     int err = hexagon::Controller::getInstance().snpprint(
     95         mGraphId, reinterpret_cast<uint8_t*>(buffer), sizeof(buffer));
     96     HEXAGON_SOFT_ASSERT_EQ(0, err, "failed getGraph");
     97     return buffer;
     98 }
     99 
    100 uint32_t Model::getNextNode() {
    101     return ++mNodeCount;
    102 }
    103 
    104 const int32_t* Model::getPointer(uint32_t operand) {
    105     return reinterpret_cast<const int32_t*>(mOperands[operand].buffer);
    106 }
    107 
    108 Shape Model::getShape(uint32_t operand) {
    109     return {
    110         .type = mOperands[operand].type,
    111         .dimensions = mOperands[operand].dimensions,
    112         .scale = mOperands[operand].scale,
    113         .offset = mOperands[operand].zeroPoint,
    114     };
    115 }
    116 
    117 bool Model::setShape(uint32_t operand, const Shape& shape) {
    118     const hexagon_nn_output& output = mOperands[operand].hexagon_output;
    119     HEXAGON_SOFT_ASSERT_EQ(output, hexagon_nn_output{}, "Output has already been set");
    120     // mOperands[operand].type       = shape.type;
    121     mOperands[operand].dimensions = shape.dimensions;
    122     // mOperands[operand].scale      = shape.scale;
    123     // mOperands[operand].zeroPoint  = shape.offset;
    124     return true;
    125 }
    126 
    127 bool Model::isConstant(uint32_t operand) {
    128     OperandLifeTime lifetime = mOperands[operand].lifetime;
    129     return lifetime == OperandLifeTime::CONSTANT_COPY ||
    130            lifetime == OperandLifeTime::CONSTANT_REFERENCE;
    131 }
    132 
    133 hexagon_nn_input Model::createTensorInternal(uint32_t B, uint32_t H, uint32_t W, uint32_t D,
    134                                              const uint8_t* ptr, size_t size) {
    135     uint32_t node = getNextNode();
    136     bool success = hexagon::Controller::getInstance().append_const_node(mGraphId, node, B, H, W, D,
    137                                                                         ptr, size) == 0;
    138     HEXAGON_SOFT_ASSERT(success, "Failed to create tensor");
    139     return {.src_id = node, .output_idx = 0};
    140 }
    141 
    142 hexagon_nn_input Model::createShape(uint32_t B, uint32_t H, uint32_t W, uint32_t D) {
    143     uint32_t dump = 0;
    144     return createTensorInternal(B, H, W, D, reinterpret_cast<uint8_t*>(&dump), sizeof(dump));
    145 }
    146 
    147 hexagon_nn_input Model::addOperand(uint32_t operandIndex) {
    148     const OperandInfo& operand = mOperands[operandIndex];
    149     std::vector<uint32_t> dims = getAlignedDimensions(operand.dimensions, 4);
    150     HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Rank must be at most 4");
    151     hexagon_nn_input result =
    152         createTensorInternal(dims[0], dims[1], dims[2], dims[3], operand.buffer, operand.length);
    153     HEXAGON_SOFT_ASSERT_NE(hexagon_nn_input{}, result, "Failed to add operand");
    154     return result;
    155 }
    156 
    157 const hexagon_nn_input& Model::getTensor(uint32_t operand) {
    158     hexagon_nn_input& tensor = mOperands[operand].hexagon_input;
    159     if (tensor == hexagon_nn_input{}) {
    160         tensor = addOperand(operand);
    161     }
    162     return tensor;
    163 }
    164 
    165 const hexagon_nn_input& Model::getQuantizationMin(uint32_t operand) {
    166     OperandInfo& operandInfo = mOperands[operand];
    167     if (operandInfo.hexagon_input_min == hexagon_nn_input{}) {
    168         float real_value =
    169             operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM
    170                 ? (std::numeric_limits<uint8_t>::min() - operandInfo.zeroPoint) * operandInfo.scale
    171                 : std::numeric_limits<uint32_t>::min() * operandInfo.scale;
    172         operandInfo.hexagon_input_min = createValues<float>({real_value});
    173     }
    174     return operandInfo.hexagon_input_min;
    175 }
    176 
    177 const hexagon_nn_input& Model::getQuantizationMax(uint32_t operand) {
    178     OperandInfo& operandInfo = mOperands[operand];
    179     if (operandInfo.hexagon_input_max == hexagon_nn_input{}) {
    180         float real_value =
    181             operandInfo.type == OperandType::TENSOR_QUANT8_ASYMM
    182                 ? (std::numeric_limits<uint8_t>::max() - operandInfo.zeroPoint) * operandInfo.scale
    183                 : std::numeric_limits<uint32_t>::max() * operandInfo.scale;
    184         operandInfo.hexagon_input_max = createValues<float>({real_value});
    185     }
    186     return operandInfo.hexagon_input_max;
    187 }
    188 
    189 hexagon_nn_padding_type Model::getPadding(uint32_t operand) {
    190     const int32_t padding = getScalar<int32_t>(operand);
    191     return hexagon::getPadding(padding);
    192 }
    193 
    194 hexagon_nn_input Model::createQuantizationValue(uint32_t operand, int32_t quant_value) {
    195     OperandInfo& operandInfo = mOperands[operand];
    196     float real_value = (quant_value - operandInfo.zeroPoint) * operandInfo.scale;
    197     return createValues<float>({real_value});
    198 }
    199 
    200 hexagon_nn_input Model::createConvFilterTensor(uint32_t operand) {
    201     OperandInfo& operandInfo = mOperands[operand];
    202     std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
    203     HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
    204     // NHWC --> HWCN
    205     if (getShape(operand).type == OperandType::TENSOR_FLOAT32) {
    206         std::vector<float> transposed =
    207             transpose<float>(dims[0], dims[1] * dims[2] * dims[3],
    208                              reinterpret_cast<const float*>(operandInfo.buffer));
    209         return createTensorInternal(dims[1], dims[2], dims[3], dims[0],
    210                                     reinterpret_cast<const uint8_t*>(transposed.data()),
    211                                     operandInfo.length);
    212     } else {
    213         std::vector<uint8_t> transposed =
    214             transpose<uint8_t>(dims[0], dims[1] * dims[2] * dims[3],
    215                                reinterpret_cast<const uint8_t*>(operandInfo.buffer));
    216         return createTensorInternal(dims[1], dims[2], dims[3], dims[0],
    217                                     reinterpret_cast<const uint8_t*>(transposed.data()),
    218                                     operandInfo.length);
    219     }
    220 }
    221 
    222 hexagon_nn_input Model::createDepthwiseFilterTensor(uint32_t operand, int32_t depth_multiplier) {
    223     OperandInfo& operandInfo = mOperands[operand];
    224     std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
    225     HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
    226     // NHWC --> HWCN
    227     return createTensorInternal(dims[1], dims[2], dims[3] / depth_multiplier,
    228                                 dims[0] * depth_multiplier, operandInfo.buffer, operandInfo.length);
    229 }
    230 
    231 hexagon_nn_input Model::createFullyConnectedWeightTensor(uint32_t operand) {
    232     OperandInfo& operandInfo = mOperands[operand];
    233     std::vector<uint32_t> dims = getAlignedDimensions(mOperands[operand].dimensions, 4);
    234     HEXAGON_SOFT_ASSERT_NE(0ul, dims.size(), "Need at most 4 dimensions");
    235     // WC --> CW
    236     uint32_t num_units = dims[0] * dims[1] * dims[2];
    237     uint32_t input_size = dims[3];
    238     if (getShape(operand).type == OperandType::TENSOR_FLOAT32) {
    239         std::vector<float> transposed = transpose<float>(
    240             num_units, input_size, reinterpret_cast<const float*>(operandInfo.buffer));
    241         return createTensorInternal(1, 1, input_size, num_units,
    242                                     reinterpret_cast<const uint8_t*>(transposed.data()),
    243                                     operandInfo.length);
    244     } else {
    245         std::vector<uint8_t> transposed = transpose<uint8_t>(
    246             num_units, input_size, reinterpret_cast<const uint8_t*>(operandInfo.buffer));
    247         return createTensorInternal(1, 1, input_size, num_units,
    248                                     reinterpret_cast<const uint8_t*>(transposed.data()),
    249                                     operandInfo.length);
    250     }
    251 }
    252 
    253 op_type Model::getFloatActivation(uint32_t operand) {
    254     return getFloatActivationFunction(getScalar<FusedActivationFunc>(operand));
    255 }
    256 
    257 op_type Model::getQuantizedActivation(uint32_t operand) {
    258     return getQuantizedActivationFunction(getScalar<FusedActivationFunc>(operand));
    259 }
    260 
    261 static bool verifyOperationInputs(const std::vector<hexagon_nn_input>& inputs) {
    262     for (const hexagon_nn_input& input : inputs) {
    263         if (input == hexagon_nn_input{}) {
    264             return false;
    265         }
    266     }
    267     return true;
    268 }
    269 
    270 static bool verifyOperationOutputs(const std::vector<hexagon_nn_output>& outputs) {
    271     for (const hexagon_nn_output& output : outputs) {
    272         if (output == hexagon_nn_output{}) {
    273             return false;
    274         }
    275     }
    276     return true;
    277 }
    278 
    279 uint32_t Model::addOperationInternal(op_type op, hexagon_nn_padding_type pad,
    280                                      const std::vector<hexagon_nn_input>& inputs,
    281                                      const std::vector<hexagon_nn_output>& outputs) {
    282     HEXAGON_SOFT_ASSERT(verifyOperationInputs(inputs),
    283                         "error adding operation: one or more inputs is invalid");
    284     HEXAGON_SOFT_ASSERT(verifyOperationOutputs(outputs),
    285                         "error adding operation: one or more outputs is invalid");
    286     uint32_t node = getNextNode();
    287     return hexagon::Controller::getInstance().append_node(mGraphId, node, op, pad, inputs.data(),
    288                                                           inputs.size(), outputs.data(),
    289                                                           outputs.size()) == 0
    290                ? node
    291                : 0;
    292 }
    293 
    294 std::vector<hexagon_nn_output> Model::getHexagonOutputs(const std::vector<uint32_t>& operands) {
    295     std::vector<hexagon_nn_output> outputs;
    296     for (uint32_t index : operands) {
    297         const OperandInfo& operand = mOperands[index];
    298         outputs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type)));
    299         if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
    300             outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)));
    301             outputs.push_back(make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)));
    302         }
    303     }
    304     return outputs;
    305 }
    306 
    307 bool Model::registerHexagonInputs(const std::vector<uint32_t>& operands, uint32_t node) {
    308     uint32_t idx = 0;
    309     for (uint32_t i = 0; i < static_cast<uint32_t>(operands.size()); ++i) {
    310         OperandInfo& operand = mOperands[operands[i]];
    311         HEXAGON_SOFT_ASSERT_EQ(operand.hexagon_input, hexagon_nn_input{},
    312                                "Error: operation output has already been registered");
    313         operand.hexagon_input = {.src_id = node, .output_idx = idx++};
    314         if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
    315             operand.hexagon_input_min = {.src_id = node, .output_idx = idx++};
    316             operand.hexagon_input_max = {.src_id = node, .output_idx = idx++};
    317         }
    318     }
    319     return true;
    320 }
    321 
    322 bool Model::addBasicOperation(op_type op, hexagon_nn_padding_type pad,
    323                               const std::vector<hexagon_nn_input>& inputs,
    324                               const std::vector<uint32_t>& outputs) {
    325     std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
    326     uint32_t node = addOperationInternal(op, pad, inputs, outs);
    327     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
    328     return registerHexagonInputs(outputs, node);
    329 }
    330 
    331 std::vector<hexagon_nn_input> Model::setupActivationArgs(op_type op) {
    332     switch (op) {
    333         case OP_Nop:
    334             return {};
    335         case OP_Relu_f:
    336             FALLTHROUGH_INTENDED;
    337         case OP_QuantizedRelu_8:
    338             return {};
    339         case OP_ReluX_f:
    340             FALLTHROUGH_INTENDED;
    341         case OP_QuantizedReluX_8:
    342             return {createValues<float>({6.0f})};
    343         case OP_Clamp_f:
    344             FALLTHROUGH_INTENDED;
    345         case OP_QuantizedClamp_8:
    346             return {createValues<float>({-1.0f}), createValues<float>({1.0f})};
    347         default:
    348             HEXAGON_SOFT_ASSERT(false, "Unknown activation symbol " << op);
    349     }
    350 }
    351 
    352 bool Model::addFloatOperationWithActivation(op_type op, hexagon_nn_padding_type pad,
    353                                             op_type activation,
    354                                             const std::vector<hexagon_nn_input>& inputs,
    355                                             const std::vector<uint32_t>& outputs) {
    356     std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
    357     std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
    358 
    359     uint32_t node = addOperationInternal(op, pad, inputs, outs);
    360     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
    361 
    362     std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0}};
    363     buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end());
    364     node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs);
    365     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
    366 
    367     return registerHexagonInputs(outputs, node);
    368 }
    369 
    370 bool Model::addQuant8OperationWithActivation(op_type op, hexagon_nn_padding_type pad,
    371                                              op_type activation,
    372                                              const std::vector<hexagon_nn_input>& inputs,
    373                                              const std::vector<uint32_t>& outputs) {
    374     std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
    375     std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
    376 
    377     uint32_t node = addOperationInternal(op, pad, inputs, outs);
    378     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
    379 
    380     std::vector<hexagon_nn_input> buffer_in = {{.src_id = node, .output_idx = 0},
    381                                                {.src_id = node, .output_idx = 1},
    382                                                {.src_id = node, .output_idx = 2}};
    383     buffer_in.insert(buffer_in.end(), actArgs.begin(), actArgs.end());
    384     node = addOperationInternal(activation, NN_PAD_NA, buffer_in, outs);
    385     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
    386 
    387     return registerHexagonInputs(outputs, node);
    388 }
    389 
    390 bool Model::addFusedFloatOperation(op_type op, hexagon_nn_padding_type pad,
    391                                    const hexagon_nn_input& bias, op_type activation,
    392                                    const std::vector<hexagon_nn_input>& inputs,
    393                                    const std::vector<uint32_t>& outputs) {
    394     HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedFloatOperation requires 1 output");
    395     std::vector<hexagon_nn_output> outs = getHexagonOutputs(outputs);
    396     std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
    397     uint32_t node;
    398 
    399     node = addOperationInternal(op, pad, inputs, outs);
    400     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
    401 
    402     if (bias != hexagon_nn_input{}) {
    403         const hexagon_nn_input buffer1_in = {.src_id = node, .output_idx = 0};
    404         node = addOperationInternal(OP_BiasAdd_f, NN_PAD_NA, {buffer1_in, bias}, outs);
    405         HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation");
    406     }
    407 
    408     if (activation != OP_Nop) {
    409         std::vector<hexagon_nn_input> buffer2_in = {{.src_id = node, .output_idx = 0}};
    410         buffer2_in.insert(buffer2_in.end(), actArgs.begin(), actArgs.end());
    411         node = addOperationInternal(activation, NN_PAD_NA, buffer2_in, outs);
    412         HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
    413     }
    414 
    415     return registerHexagonInputs(outputs, node);
    416 }
    417 
    418 bool Model::addFusedQuant8Operation(op_type op, hexagon_nn_padding_type pad,
    419                                     const std::vector<hexagon_nn_input>& bias, op_type activation,
    420                                     const std::vector<hexagon_nn_input>& inputs,
    421                                     const std::vector<uint32_t>& outputs) {
    422     HEXAGON_SOFT_ASSERT_EQ(1, outputs.size(), "addFusedQuant8Operation requires 1 output");
    423     std::vector<hexagon_nn_input> actArgs = setupActivationArgs(activation);
    424     const hexagon_nn_input& new_min = getQuantizationMin(outputs[0]);
    425     const hexagon_nn_input& new_max = getQuantizationMax(outputs[0]);
    426     uint32_t node;
    427 
    428     hexagon_nn_output tensor_out8 =
    429         make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(uint8_t));
    430     hexagon_nn_output tensor_out32 =
    431         make_hexagon_nn_output(mOperands[outputs[0]].dimensions, sizeof(int32_t));
    432     hexagon_nn_output scalar_out32 = make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float));
    433 
    434     std::vector<hexagon_nn_output> out8 = {tensor_out8, scalar_out32, scalar_out32};
    435     std::vector<hexagon_nn_output> out32 = {tensor_out32, scalar_out32, scalar_out32};
    436 
    437     // base operation
    438     node = addOperationInternal(op, pad, inputs, out32);
    439     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding base operation");
    440     hexagon_nn_input previous = {.src_id = node, .output_idx = 0};
    441     hexagon_nn_input previous_min = {.src_id = node, .output_idx = 1};
    442     hexagon_nn_input previous_max = {.src_id = node, .output_idx = 2};
    443 
    444     // add bias
    445     if (bias.size() == 3) {
    446         node = addOperationInternal(
    447             OP_QuantizedBiasAdd_32p32to32, NN_PAD_NA,
    448             {previous, bias[0], previous_min, previous_max, bias[1], bias[2]}, out32);
    449         HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding bias operation");
    450         previous.src_id = node;
    451         previous_min.src_id = node;
    452         previous_max.src_id = node;
    453     }
    454 
    455     // requantize
    456     node = addOperationInternal(OP_Requantize_32to8, NN_PAD_NA,
    457                                 {previous, previous_min, previous_max, new_min, new_max}, out8);
    458     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding requantize operation");
    459     previous.src_id = node;
    460     previous_min.src_id = node;
    461     previous_max.src_id = node;
    462 
    463     // activation
    464     if (activation != OP_Nop) {
    465         std::vector<hexagon_nn_input> buffer = {previous, previous_min, previous_max};
    466         buffer.insert(buffer.end(), actArgs.begin(), actArgs.end());
    467         node = addOperationInternal(activation, NN_PAD_NA, buffer, out8);
    468         HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding activation operation");
    469     }
    470 
    471     return registerHexagonInputs(outputs, node);
    472 }
    473 
    474 bool Model::verifyOperations() {
    475     std::vector<bool> supported = supportedOperations();
    476     return std::all_of(supported.begin(), supported.end(), [](bool valid) { return valid; });
    477 }
    478 
    479 bool Model::verifyOperands() {
    480     for (const OperandInfo& operand : mOperands) {
    481         HEXAGON_SOFT_ASSERT_GE(4ul, operand.dimensions.size(),
    482                                "Operand must have at most 4 dimensions");
    483         for (uint32_t dim : operand.dimensions) {
    484             HEXAGON_SOFT_ASSERT_NE(0, dim, "At least one operand with unknown dimension");
    485         }
    486     }
    487     return true;
    488 }
    489 
    490 bool Model::addInputs() {
    491     // prepare OP_INPUT's outputs
    492     std::vector<hexagon_nn_output> outs;
    493     for (size_t i = 0; i < mInputs.size(); ++i) {
    494         OperandInfo& operand = mOperands[mInputs[i]];
    495         outs.push_back(make_hexagon_nn_output(operand.dimensions, getSize(operand.type)));
    496     }
    497 
    498     // add single input node for entire graph
    499     uint32_t node = addOperationInternal(OP_INPUT, NN_PAD_NA, {}, outs);
    500     HEXAGON_SOFT_ASSERT_NE(0, node, "Error adding input operation");
    501 
    502     // update operand information
    503     for (size_t i = 0; i < mInputs.size(); ++i) {
    504         OperandInfo& operand = mOperands[mInputs[i]];
    505         operand.hexagon_input = {.src_id = node, .output_idx = static_cast<uint32_t>(i)};
    506     }
    507 
    508     return true;
    509 }
    510 
    511 bool Model::addOperations() {
    512     for (const Operation& operation : mOperations) {
    513         OperationType operationType = operation.type;
    514 
    515         // For now, the operation type is always the same as its first operand
    516         // parameter. If this changes in the future, this line of code will need
    517         // to be updated.
    518         OperandType operandType = mOperands[operation.inputs[0]].type;
    519 
    520         OperationTuple opTuple = std::make_pair(operationType, operandType);
    521         HEXAGON_SOFT_ASSERT(
    522             getOperationPrepareTable().find(opTuple) != getOperationPrepareTable().end(),
    523             "Operation not found");
    524         bool success =
    525             getOperationPrepareTable()[opTuple](operation.inputs, operation.outputs, this);
    526         HEXAGON_SOFT_ASSERT(success, "error adding operation");
    527     }
    528     return true;
    529 }
    530 
    531 bool Model::addOutputs() {
    532     // prepare OP_OUTPUT's inputs
    533     std::vector<hexagon_nn_input> ins;
    534     for (size_t out : mOutputs) {
    535         OperandInfo& operand = mOperands[out];
    536         HEXAGON_SOFT_ASSERT_NE(operand.hexagon_input, hexagon_nn_input{},
    537                                "output operand has not been registered");
    538 
    539         if (operand.type == OperandType::TENSOR_QUANT8_ASYMM) {
    540             // Adjust quantized range of outputs
    541             uint32_t dequant = addOperationInternal(
    542                 OP_Dequantize, NN_PAD_NA,
    543                 {operand.hexagon_input, operand.hexagon_input_min, operand.hexagon_input_max},
    544                 {make_hexagon_nn_output(operand.dimensions, sizeof(float))});
    545             uint32_t quant =
    546                 addOperationInternal(OP_Quantize, NN_PAD_NA,
    547                                      {{.src_id = dequant, .output_idx = 0},
    548                                       createQuantizationValue(out, 0),
    549                                       createQuantizationValue(out, 255)},
    550                                      {make_hexagon_nn_output(operand.dimensions, sizeof(uint8_t)),
    551                                       make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float)),
    552                                       make_hexagon_nn_output({1, 1, 1, 1}, sizeof(float))});
    553             ins.push_back({.src_id = quant, .output_idx = 0});
    554         } else {
    555             ins.push_back(operand.hexagon_input);
    556         }
    557     }
    558 
    559     // add single output node for entire graph
    560     bool success = addBasicOperation(OP_OUTPUT, NN_PAD_NA, ins, {});
    561     HEXAGON_SOFT_ASSERT(success, "Error adding output operation");
    562 
    563     return true;
    564 }
    565 
    566 void Model::clearModel() {
    567     mCompiled = false;
    568     for (OperandInfo& operand : mOperands) {
    569         operand.hexagon_input = {};
    570         operand.hexagon_input_min = {};
    571         operand.hexagon_input_max = {};
    572         operand.hexagon_output = {};
    573     }
    574     if (mGraphId != hexagon_nn_nn_id{}) {
    575         hexagon::Controller::getInstance().teardown(mGraphId);
    576     }
    577 }
    578 
    579 std::vector<bool> Model::supportedOperations() {
    580     std::vector<bool> supported(mOperations.size());
    581     for (size_t i = 0; i < supported.size(); ++i) {
    582         const Operation& operation = mOperations[i];
    583         OperationType operationType = operation.type;
    584 
    585         // For now, the operation type is always the same as its first operand
    586         // parameter. If this changes in the future, this line of code will need
    587         // to be updated.
    588         OperandType operandType = mOperands[operation.inputs[0]].type;
    589 
    590         OperationTuple opTuple = std::make_pair(operationType, operandType);
    591 
    592         auto entry = getOperationCheckTable().find(opTuple);
    593         if (entry != getOperationCheckTable().end()) {
    594             supported[i] = entry->second(operation.inputs, operation.outputs, this);
    595         } else {
    596             supported[i] = false;
    597         }
    598     }
    599     return supported;
    600 }
    601 
    602 bool Model::prepare() {
    603     if (!verifyOperations() || !verifyOperands()) {
    604         return false;
    605     }
    606 
    607     int err = hexagon::Controller::getInstance().init(&mGraphId);
    608     HEXAGON_SOFT_ASSERT_EQ(0, err, "Hexagon could not allocate new graph");
    609     HEXAGON_SOFT_ASSERT_NE(0, mGraphId, "Hexagon could not allocate new graph");
    610     hexagon::Controller::getInstance().set_debug_level(mGraphId, 0);
    611 
    612     if (!addInputs() || !addOperations() || !addOutputs()) {
    613         clearModel();
    614         LOG(ERROR) << "Something went wrong. Clearing the model and aborting.";
    615         return false;
    616     }
    617 
    618     err = hexagon::Controller::getInstance().prepare(mGraphId);
    619 
    620     LOG(INFO) << "PrepareModel was " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL");
    621 
    622     return err == 0;
    623 }
    624 
    625 static hexagon_nn_tensordef convertToTensordef(const OperandInfo& operand) {
    626     std::vector<uint32_t> dimensions = getAlignedDimensions(operand.dimensions, 4);
    627     return {
    628         .batches = dimensions[0],
    629         .height = dimensions[1],
    630         .width = dimensions[2],
    631         .depth = dimensions[3],
    632         .data = operand.buffer,
    633         .dataLen = static_cast<int32_t>(operand.length),
    634         .data_valid_len = operand.length,  // unused?
    635         .unused = 0,
    636     };
    637 }
    638 
    639 static uint32_t getSize(const OperandInfo& operand) {
    640     return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(),
    641                            getSize(operand.type), std::multiplies<>{});
    642 }
    643 
    644 static OperandInfo getUpdatedOperand(const RequestArgument& inputOutput,
    645                                      const std::vector<RunTimePoolInfo>& pools,
    646                                      const OperandInfo& oldInfo) {
    647     OperandInfo newInfo = oldInfo;
    648 
    649     const RunTimePoolInfo& pool = pools[inputOutput.location.poolIndex];
    650     uint32_t offset = inputOutput.location.offset;
    651 
    652     if (inputOutput.dimensions.size() > 0) {
    653         newInfo.dimensions = inputOutput.dimensions;
    654     }
    655 
    656     newInfo.buffer = pool.getBuffer() + offset;
    657     newInfo.length = getSize(newInfo);
    658 
    659     return newInfo;
    660 }
    661 
    662 bool Model::execute(const Request& request) {
    663     std::vector<RunTimePoolInfo> pools = mapPools(request.pools);
    664 
    665     // prepare inputs
    666     std::vector<hexagon_nn_tensordef> inputs;
    667     for (size_t i = 0; i < request.inputs.size(); ++i) {
    668         const OperandInfo& oldInfo = mOperands[mInputs[i]];
    669         OperandInfo newInfo = getUpdatedOperand(request.inputs[i], pools, oldInfo);
    670         inputs.push_back(convertToTensordef(newInfo));
    671     }
    672 
    673     // prepare outputs
    674     std::vector<hexagon_nn_tensordef> outputs;
    675     for (size_t i = 0; i < request.outputs.size(); ++i) {
    676         const OperandInfo& oldInfo = mOperands[mOutputs[i]];
    677         OperandInfo newInfo = getUpdatedOperand(request.outputs[i], pools, oldInfo);
    678         outputs.push_back(convertToTensordef(newInfo));
    679     }
    680 
    681     // execute model
    682     int err = hexagon::Controller::getInstance().execute_new(mGraphId, inputs.data(), inputs.size(),
    683                                                              outputs.data(), outputs.size());
    684 
    685     std::for_each(pools.begin(), pools.end(), [](RunTimePoolInfo& pool) { pool.update(); });
    686 
    687     LOG(INFO) << "EXECUTION WAS " << (err == 0 ? "SUCCESSFUL" : "UNSUCCESSFUL");
    688 
    689     return err == 0;
    690 }
    691 
    692 }  // namespace hexagon
    693 }  // namespace implementation
    694 }  // namespace V1_0
    695 }  // namespace neuralnetworks
    696 }  // namespace hardware
    697 }  // namespace android
    698