C++程序  |  217行  |  9.04 KB

/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "fuzzing/operation_signatures/OperationSignatureUtils.h"

namespace android {
namespace nn {
namespace fuzzing_test {

static void roiTensorConstructor(Type dataType, uint32_t, RandomOperand* op) {
    op->dataType = dataType;
    if (dataType == Type::TENSOR_QUANT8_ASYMM) {
        op->dataType = Type::TENSOR_QUANT16_ASYMM;
        op->scale = 0.125f;
        op->zeroPoint = 0;
    }
}

// TODO: Have a version that makes roi tensor an input.
static const OperandSignature kInputRoiTensor = {.type = RandomOperandType::CONST,
                                                 .constructor = roiTensorConstructor};
static const OperandSignature kOutputRoiTensor = {.type = RandomOperandType::OUTPUT,
                                                  .constructor = roiTensorConstructor};

static void roiConstructor(Type, uint32_t rank, RandomOperation* op) {
    NN_FUZZER_CHECK(rank == 4);
    bool useNchw;
    if (op->opType == ANEURALNETWORKS_ROI_ALIGN) {
        useNchw = op->inputs[9]->value<bool8>();
    } else {
        useNchw = op->inputs[7]->value<bool8>();
    }

    op->inputs[0]->dimensions = {RandomVariableType::FREE, RandomVariableType::FREE,
                                 RandomVariableType::FREE, RandomVariableType::FREE};
    op->inputs[1]->dimensions = {RandomVariableType::FREE, 4};
    op->inputs[2]->dimensions = {op->inputs[1]->dimensions[0]};
    auto outBatch = op->inputs[1]->dimensions[0];
    auto outDepth = op->inputs[0]->dimensions[useNchw ? 1 : 3];
    auto outHeight = op->inputs[3]->value<RandomVariable>();
    auto outWidth = op->inputs[4]->value<RandomVariable>();
    if (useNchw) {
        op->outputs[0]->dimensions = {outBatch, outDepth, outHeight, outWidth};
    } else {
        op->outputs[0]->dimensions = {outBatch, outHeight, outWidth, outDepth};
    }

    if (op->opType == ANEURALNETWORKS_ROI_POOLING) {
        setSameQuantization(op->outputs[0], op->inputs[0]);
    }
}

template <typename T>
inline void fillRoiTensor(uint32_t numRois, T maxH, T maxW, RandomOperand* op) {
    for (uint32_t i = 0; i < numRois; i++) {
        T low = getUniform<T>(0, maxW);
        op->value<T>(i * 4) = low;
        op->value<T>(i * 4 + 2) = getUniform<T>(low, maxW);
        low = getUniform<T>(0, maxH);
        op->value<T>(i * 4 + 1) = low;
        op->value<T>(i * 4 + 3) = getUniform<T>(low, maxH);
    }
}

static void roiFinalizer(RandomOperation* op) {
    bool useNchw;
    if (op->opType == ANEURALNETWORKS_ROI_ALIGN) {
        useNchw = op->inputs[9]->value<bool8>();
    } else {
        useNchw = op->inputs[7]->value<bool8>();
    }

    uint32_t batch = op->inputs[0]->dimensions[0].getValue();
    uint32_t height = op->inputs[0]->dimensions[useNchw ? 2 : 1].getValue();
    uint32_t width = op->inputs[0]->dimensions[useNchw ? 3 : 2].getValue();
    uint32_t numRois = op->inputs[1]->dimensions[0].getValue();
    // Fill values to the roi tensor with format [x1, y1, x2, y2].
    switch (op->inputs[1]->dataType) {
        case Type::TENSOR_FLOAT32: {
            float maxH = static_cast<float>(height) * op->inputs[5]->value<float>();
            float maxW = static_cast<float>(width) * op->inputs[6]->value<float>();
            fillRoiTensor<float>(numRois, maxH, maxW, op->inputs[1].get());
        } break;
        case Type::TENSOR_QUANT16_ASYMM: {
            uint16_t maxH = static_cast<float>(height) * op->inputs[5]->value<float>();
            uint16_t maxW = static_cast<float>(width) * op->inputs[6]->value<float>();
            fillRoiTensor<uint16_t>(numRois, maxH, maxW, op->inputs[1].get());

        } break;
        default:
            NN_FUZZER_CHECK(false) << "Unsupported data type.";
    }

    // Fill values to the batch index tensor.
    std::vector<int32_t> batchIndex(numRois);
    for (uint32_t i = 0; i < numRois; i++) batchIndex[i] = getUniform<int32_t>(0, batch - 1);
    // Same batches are grouped together.
    std::sort(batchIndex.begin(), batchIndex.end());
    for (uint32_t i = 0; i < numRois; i++) op->inputs[2]->value<int32_t>(i) = batchIndex[i];
}

// Type::TENSOR_FLOAT16 is intentionally excluded for all bounding box ops because
// 1. It has limited precision for compuation on bounding box indices, which will lead to poor
//    accuracy evaluation.
// 2. There is no actual graph that uses this data type on bounding boxes.

DEFINE_OPERATION_SIGNATURE(ROI_ALIGN_V1_2){
        .opType = ANEURALNETWORKS_ROI_ALIGN,
        .supportedDataTypes = {Type::TENSOR_FLOAT32, Type::TENSOR_QUANT8_ASYMM},
        .supportedRanks = {4},
        .version = HalVersion::V1_2,
        .inputs =
                {
                        INPUT_DEFAULT,
                        kInputRoiTensor,
                        PARAMETER_NONE(Type::TENSOR_INT32),
                        RANDOM_INT_FREE,
                        RANDOM_INT_FREE,
                        PARAMETER_FLOAT_RANGE(0.1f, 10.0f),
                        PARAMETER_FLOAT_RANGE(0.1f, 10.0f),
                        PARAMETER_RANGE(Type::INT32, 0, 10),
                        PARAMETER_RANGE(Type::INT32, 0, 10),
                        PARAMETER_CHOICE(Type::BOOL, true, false),
                },
        .outputs = {OUTPUT_DEFAULT},
        .constructor = roiConstructor,
        .finalizer = roiFinalizer};

DEFINE_OPERATION_SIGNATURE(ROI_POOLING_V1_2){
        .opType = ANEURALNETWORKS_ROI_POOLING,
        .supportedDataTypes = {Type::TENSOR_FLOAT32, Type::TENSOR_QUANT8_ASYMM},
        .supportedRanks = {4},
        .version = HalVersion::V1_2,
        .inputs =
                {
                        INPUT_DEFAULT,
                        kInputRoiTensor,
                        PARAMETER_NONE(Type::TENSOR_INT32),
                        RANDOM_INT_FREE,
                        RANDOM_INT_FREE,
                        PARAMETER_FLOAT_RANGE(0.1f, 10.0f),
                        PARAMETER_FLOAT_RANGE(0.1f, 10.0f),
                        PARAMETER_CHOICE(Type::BOOL, true, false),
                },
        .outputs = {OUTPUT_DEFAULT},
        .constructor = roiConstructor,
        .finalizer = roiFinalizer};

static void heatmapMaxKeypointConstructor(Type, uint32_t rank, RandomOperation* op) {
    NN_FUZZER_CHECK(rank == 4);
    bool useNchw = op->inputs[2]->value<bool8>();
    RandomVariable heatmapSize = RandomVariableType::FREE;
    RandomVariable numRois = RandomVariableType::FREE;
    RandomVariable numKeypoints = RandomVariableType::FREE;
    heatmapSize.setRange(2, kInvalidValue);

    if (useNchw) {
        op->inputs[0]->dimensions = {numRois, numKeypoints, heatmapSize, heatmapSize};
    } else {
        op->inputs[0]->dimensions = {numRois, heatmapSize, heatmapSize, numKeypoints};
    }
    op->inputs[1]->dimensions = {numRois, 4};
    op->outputs[0]->dimensions = {numRois, numKeypoints};
    op->outputs[1]->dimensions = {numRois, numKeypoints, 2};

    // TODO: This is an ugly fix due to the limitation of the current generator that can not handle
    // the dimension dependency within an input. Without the following line, most of the generated
    // HEATMAP_MAX_KEYPOINT graphs will be invalid and triggers retry.
    RandomVariableNetwork::get()->addDimensionProd(
            {numRois, numKeypoints, heatmapSize * heatmapSize});
}

static void heatmapMaxKeypointFinalizer(RandomOperation* op) {
    uint32_t numRois = op->inputs[0]->dimensions[0].getValue();
    uint32_t heatmapSize = op->inputs[0]->dimensions[2].getValue();
    // Fill values to the roi tensor with format [x1, y1, x2, y2].
    switch (op->inputs[1]->dataType) {
        case Type::TENSOR_FLOAT32: {
            float maxSize = heatmapSize;
            fillRoiTensor<float>(numRois, maxSize, maxSize, op->inputs[1].get());
        } break;
        case Type::TENSOR_QUANT16_ASYMM: {
            uint16_t maxSize = static_cast<uint16_t>(heatmapSize * 8);
            fillRoiTensor<uint16_t>(numRois, maxSize, maxSize, op->inputs[1].get());
        } break;
        default:
            NN_FUZZER_CHECK(false) << "Unsupported data type.";
    }
}

DEFINE_OPERATION_SIGNATURE(HEATMAP_MAX_KEYPOINT_V1_2){
        .opType = ANEURALNETWORKS_HEATMAP_MAX_KEYPOINT,
        .supportedDataTypes = {Type::TENSOR_FLOAT32, Type::TENSOR_QUANT8_ASYMM},
        .supportedRanks = {4},
        .version = HalVersion::V1_2,
        .inputs = {INPUT_DEFAULT, kInputRoiTensor, PARAMETER_CHOICE(Type::BOOL, true, false)},
        .outputs = {OUTPUT_DEFAULT, kOutputRoiTensor},
        .constructor = heatmapMaxKeypointConstructor,
        .finalizer = heatmapMaxKeypointFinalizer};

}  // namespace fuzzing_test
}  // namespace nn
}  // namespace android