blob: 90fb00e9a56d4a332be943066724a59ba6d1c893 [file] [log] [blame]
/*
* Copyright (C) 2017 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.
*/
#define LOG_TAG "Utils"
#include "LegacyHalUtils.h"
#include <nnapi/TypeUtils.h>
#include <nnapi/hal/1.0/Conversions.h>
#include <nnapi/hal/1.1/Conversions.h>
#include <nnapi/hal/1.2/Conversions.h>
#include <nnapi/hal/1.3/Conversions.h>
#include <algorithm>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "NeuralNetworks.h"
#include "ValidateHal.h"
namespace android {
namespace nn {
constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {.execTime = FLT_MAX, .powerUsage = FLT_MAX};
static uint64_t getMaxNanosecondsSinceEpoch() {
const auto maxTime =
std::chrono::time_point<std::chrono::steady_clock, std::chrono::nanoseconds>::max();
return maxTime.time_since_epoch().count();
}
std::optional<Deadline> makeDeadline(const V1_3::OptionalTimePoint& timePoint) {
using Discriminator = V1_3::OptionalTimePoint::hidl_discriminator;
if (timePoint.getDiscriminator() == Discriminator::none) {
return std::nullopt;
}
const uint64_t nanosecondsSinceEpoch = timePoint.nanosecondsSinceEpoch();
const uint64_t maxNanosecondsSinceEpoch = getMaxNanosecondsSinceEpoch();
// Clamp time point to max.
if (nanosecondsSinceEpoch >= maxNanosecondsSinceEpoch) {
return Deadline::max();
}
// Return provided time point.
return Deadline{std::chrono::nanoseconds{nanosecondsSinceEpoch}};
}
bool isExtensionOperandType(V1_3::OperandType type) {
return isExtensionOperandType(static_cast<OperandType>(type));
}
bool isExtensionOperationType(V1_3::OperationType type) {
return isExtensionOperationType(static_cast<OperationType>(type));
}
std::string getOperandTypeName(V1_3::OperandType type) {
return toString(type);
}
std::string getOperationName(V1_3::OperationType type) {
return toString(type);
}
uint32_t nonExtensionOperandSizeOfData(V1_3::OperandType type,
const std::vector<uint32_t>& dimensions) {
return nonExtensionOperandSizeOfData(uncheckedConvert(type), dimensions);
}
bool nonExtensionOperandSizeOfDataOverflowsUInt32(V1_3::OperandType type,
const std::vector<uint32_t>& dimensions) {
return nonExtensionOperandSizeOfDataOverflowsUInt32(uncheckedConvert(type), dimensions);
}
bool tensorHasUnspecifiedDimensions(V1_3::OperandType type,
const std::vector<uint32_t>& dimensions) {
return tensorHasUnspecifiedDimensions(static_cast<int>(type), dimensions.data(),
dimensions.size());
}
bool tensorHasUnspecifiedDimensions(const V1_3::Operand& operand) {
return tensorHasUnspecifiedDimensions(static_cast<int>(operand.type), operand.dimensions.data(),
operand.dimensions.size());
}
void logModelToInfo(const V1_0::Model& model) {
LOG(INFO) << "V1_0::Model start";
LOG(INFO) << "operands" << toString(model.operands);
LOG(INFO) << "operations" << toString(model.operations);
LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
LOG(INFO) << "operandValues size" << model.operandValues.size();
LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
}
void logModelToInfo(const V1_1::Model& model) {
LOG(INFO) << "V1_1::Model start";
LOG(INFO) << "operands" << toString(model.operands);
LOG(INFO) << "operations" << toString(model.operations);
LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
LOG(INFO) << "operandValues size " << model.operandValues.size();
LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
}
void logModelToInfo(const V1_2::Model& model) {
LOG(INFO) << "V1_2::Model start";
LOG(INFO) << "operands" << toString(model.operands);
LOG(INFO) << "operations" << toString(model.operations);
LOG(INFO) << "inputIndexes" << toString(model.inputIndexes);
LOG(INFO) << "outputIndexes" << toString(model.outputIndexes);
LOG(INFO) << "operandValues size" << model.operandValues.size();
LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
LOG(INFO) << "relaxComputationFloat32toFloat16" << model.relaxComputationFloat32toFloat16;
LOG(INFO) << "extensionNameToPrefix" << toString(model.extensionNameToPrefix);
}
static void logSubgraphToInfo(std::string label, const V1_3::Subgraph& subgraph) {
LOG(INFO) << label << ".operands" << toString(subgraph.operands);
LOG(INFO) << label << ".operations" << toString(subgraph.operations);
LOG(INFO) << label << ".inputIndexes" << toString(subgraph.inputIndexes);
LOG(INFO) << label << ".outputIndexes" << toString(subgraph.outputIndexes);
}
void logModelToInfo(const V1_3::Model& model) {
LOG(INFO) << "V1_3::Model start";
logSubgraphToInfo("main", model.main);
for (uint32_t i = 0, n = model.referenced.size(); i < n; ++i) {
logSubgraphToInfo("referenced[" + std::to_string(i) + "]", model.referenced[i]);
}
LOG(INFO) << "operandValues size " << model.operandValues.size();
LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools));
LOG(INFO) << "relaxComputationFloat32toFloat16 " << model.relaxComputationFloat32toFloat16;
LOG(INFO) << "extensionNameToPrefix" << toString(model.extensionNameToPrefix);
}
bool validateOperandSymmPerChannelQuantParams(
const V1_3::Operand& halOperand,
const ANeuralNetworksSymmPerChannelQuantParams& channelQuant, const char* tag) {
if (halOperand.type != V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
return false;
}
NN_RET_CHECK_LT(channelQuant.channelDim, halOperand.dimensions.size()) << tag;
NN_RET_CHECK(channelQuant.scales != nullptr) << tag;
NN_RET_CHECK_EQ(channelQuant.scaleCount, halOperand.dimensions[channelQuant.channelDim]) << tag;
NN_RET_CHECK_NE(halOperand.dimensions[channelQuant.channelDim], 0u)
<< tag << " channel dimension " << channelQuant.channelDim << " is underspecified";
for (uint32_t i = 0; i < halOperand.dimensions[channelQuant.channelDim]; i++) {
NN_RET_CHECK_GT(channelQuant.scales[i], 0.0f) << tag << " invalid scaleArray[" << i << "]";
}
return true;
}
static int validateHalVersion(ANeuralNetworksOperationType opType, HalVersion halVersion,
HalVersion minSupportedHalVersion) {
if (halVersion < minSupportedHalVersion) {
LOG(ERROR) << "The given inputs and outputs for operation " << opType
<< " are only supported in " << minSupportedHalVersion
<< " and later (validating using " << halVersion << ")";
return ANEURALNETWORKS_BAD_DATA;
}
return ANEURALNETWORKS_NO_ERROR;
}
static inline int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount,
const uint32_t* inputIndexes, uint32_t outputCount,
const uint32_t* outputIndexes,
const std::vector<Operand>& operands, HalVersion halVersion) {
if (opType == ANEURALNETWORKS_IF || opType == ANEURALNETWORKS_WHILE) {
NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3));
LOG(ERROR) << "This validateOperation() overload does not support control flow";
return ANEURALNETWORKS_BAD_DATA;
}
return validateOperation(opType, inputCount, inputIndexes, outputCount, outputIndexes, operands,
halVersion, {});
}
V1_3::ErrorStatus convertResultCodeToHalErrorStatus(int resultCode) {
return convertToV1_3(convertResultCodeToErrorStatus(resultCode));
}
int convertErrorStatusToResultCode(V1_3::ErrorStatus status) {
return convertErrorStatusToResultCode(uncheckedConvert(status));
}
std::tuple<int, std::vector<OutputShape>, Timing> getExecutionResult(
V1_3::ErrorStatus status, const hardware::hidl_vec<V1_2::OutputShape>& outputShapes,
const V1_2::Timing& timing) {
return getExecutionResult(uncheckedConvert(status), uncheckedConvert(outputShapes),
uncheckedConvert(timing));
}
// Capabilities::operandPerformance utilities.
// The field Capabilities::operandPerformance is a vector sorted by the field
// Capabilities::OperandPerformance::type.
template <HalVersion version>
hardware::hidl_vec<VersionedOperandPerformance<version>> nonExtensionOperandPerformance(
V1_0::PerformanceInfo perf) {
using OpPerf = VersionedOperandPerformance<version>;
// Note: range presents enumerators in declaration order, not in numerical order.
static constexpr hardware::hidl_enum_range<VersionedOperandType<version>> kOperandTypeRange;
std::vector<OpPerf> ret;
ret.reserve(kOperandTypeRange.end() - kOperandTypeRange.begin());
for (VersionedOperandType<version> type : kOperandTypeRange) {
if (static_cast<V1_3::OperandType>(type) != V1_3::OperandType::SUBGRAPH) {
ret.push_back(OpPerf{type, perf});
}
}
std::sort(ret.begin(), ret.end(),
[](const OpPerf& a, const OpPerf& b) { return a.type < b.type; });
return ret;
}
template hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>
nonExtensionOperandPerformance<HalVersion::V1_2>(V1_0::PerformanceInfo perf);
template hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>
nonExtensionOperandPerformance<HalVersion::V1_3>(V1_0::PerformanceInfo perf);
template <HalVersion version>
void update(hardware::hidl_vec<VersionedOperandPerformance<version>>* operandPerformance,
VersionedOperandType<version> type, V1_0::PerformanceInfo perf) {
CHECK(operandPerformance != nullptr);
const auto it =
std::lower_bound(operandPerformance->begin(), operandPerformance->end(), type,
[](const VersionedOperandPerformance<version>& perf,
VersionedOperandType<version> type) { return perf.type < type; });
CHECK(it != operandPerformance->end())
<< toString(type) << " not in " << toString(*operandPerformance);
it->info = perf;
}
void update(hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>* operandPerformance,
V1_2::OperandType type, V1_0::PerformanceInfo perf) {
update<HalVersion::V1_2>(operandPerformance, type, perf);
}
void update(hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>* operandPerformance,
V1_3::OperandType type, V1_0::PerformanceInfo perf) {
update<HalVersion::V1_3>(operandPerformance, type, perf);
}
template <HalVersion version>
V1_0::PerformanceInfo lookup(
const hardware::hidl_vec<VersionedOperandPerformance<version>>& operandPerformance,
VersionedOperandType<version> type) {
const auto it = std::lower_bound(operandPerformance.begin(), operandPerformance.end(), type,
[](const VersionedOperandPerformance<version>& perf,
VersionedOperandType<version> type) {
return static_cast<V1_3::OperandType>(perf.type) <
static_cast<V1_3::OperandType>(type);
});
if (it == operandPerformance.end()) {
LOG(WARNING) << "No PerformanceInfo for " << toString(type);
return kNoPerformanceInfo;
} else {
return it->info;
}
}
V1_0::PerformanceInfo lookup(
const hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>& operandPerformance,
V1_2::OperandType type) {
return lookup<HalVersion::V1_2>(operandPerformance, type);
}
V1_0::PerformanceInfo lookup(
const hardware::hidl_vec<V1_3::Capabilities::OperandPerformance>& operandPerformance,
V1_3::OperandType type) {
CHECK(type != V1_3::OperandType::SUBGRAPH)
<< "Use Capabilities::ifPerformance or Capabilities::whilePerformance";
return lookup<HalVersion::V1_3>(operandPerformance, type);
}
// Versioning
// In Android P, most data types are treated as having the same performance as TENSOR_QUANT8_ASYMM.
// This array must be in sorted order.
static const V1_3::OperandType kQuantized8PerformanceConsistentWithP[] = {
V1_3::OperandType::INT32, V1_3::OperandType::UINT32, V1_3::OperandType::TENSOR_INT32,
V1_3::OperandType::OEM, V1_3::OperandType::TENSOR_OEM_BYTE};
static bool isQuantized8PerformanceConsistentWithP(const V1_2::Capabilities& capabilities) {
const V1_0::PerformanceInfo quantized8Performance =
lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_QUANT8_ASYMM);
return std::all_of(std::begin(kQuantized8PerformanceConsistentWithP),
std::end(kQuantized8PerformanceConsistentWithP),
[quantized8Performance, &capabilities](V1_3::OperandType type) {
return quantized8Performance ==
lookup(capabilities.operandPerformance,
static_cast<V1_2::OperandType>(type));
});
}
static bool isQuantized8PerformanceConsistentWithP(const V1_3::Capabilities& capabilities) {
const V1_0::PerformanceInfo quantized8Performance =
lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_QUANT8_ASYMM);
return std::all_of(std::begin(kQuantized8PerformanceConsistentWithP),
std::end(kQuantized8PerformanceConsistentWithP),
[quantized8Performance, &capabilities](V1_3::OperandType type) {
return quantized8Performance ==
lookup(capabilities.operandPerformance, type);
});
}
static hardware::hidl_vec<V1_2::Capabilities::OperandPerformance>
makeQuantized8PerformanceConsistentWithP(V1_0::PerformanceInfo quantized8Performance) {
hardware::hidl_vec<V1_2::Capabilities::OperandPerformance> ret(
std::size(kQuantized8PerformanceConsistentWithP));
std::transform(std::begin(kQuantized8PerformanceConsistentWithP),
std::end(kQuantized8PerformanceConsistentWithP), ret.begin(),
[quantized8Performance](
V1_3::OperandType type) -> V1_2::Capabilities::OperandPerformance {
return {static_cast<V1_2::OperandType>(type), quantized8Performance};
});
return ret;
}
bool compliantWithV1_0(const V1_0::Capabilities&) {
return true;
}
bool compliantWithV1_0(const V1_1::Capabilities& capabilities) {
return capabilities.relaxedFloat32toFloat16Performance == capabilities.float32Performance;
}
bool compliantWithV1_0(const V1_2::Capabilities& capabilities) {
const V1_0::PerformanceInfo perfTensorFloat32 =
lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32);
const V1_0::PerformanceInfo perfFloat32 =
lookup(capabilities.operandPerformance, V1_2::OperandType::FLOAT32);
if (perfTensorFloat32 != perfFloat32 ||
perfTensorFloat32 != capabilities.relaxedFloat32toFloat16PerformanceTensor ||
perfFloat32 != capabilities.relaxedFloat32toFloat16PerformanceScalar) {
return false;
}
return isQuantized8PerformanceConsistentWithP(capabilities);
}
bool compliantWithV1_0(const V1_3::Capabilities& capabilities) {
const V1_0::PerformanceInfo perfTensorFloat32 =
lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32);
const V1_0::PerformanceInfo perfFloat32 =
lookup(capabilities.operandPerformance, V1_3::OperandType::FLOAT32);
if (perfTensorFloat32 != perfFloat32 ||
perfTensorFloat32 != capabilities.relaxedFloat32toFloat16PerformanceTensor ||
perfFloat32 != capabilities.relaxedFloat32toFloat16PerformanceScalar) {
return false;
}
return isQuantized8PerformanceConsistentWithP(capabilities);
}
bool compliantWithV1_1(const V1_0::Capabilities&) {
return true;
}
bool compliantWithV1_1(const V1_1::Capabilities&) {
return true;
}
bool compliantWithV1_1(const V1_2::Capabilities& capabilities) {
if ((capabilities.relaxedFloat32toFloat16PerformanceTensor !=
capabilities.relaxedFloat32toFloat16PerformanceScalar) ||
(lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32) !=
lookup(capabilities.operandPerformance, V1_2::OperandType::FLOAT32))) {
return false;
}
return isQuantized8PerformanceConsistentWithP(capabilities);
}
bool compliantWithV1_1(const V1_3::Capabilities& capabilities) {
if ((capabilities.relaxedFloat32toFloat16PerformanceTensor !=
capabilities.relaxedFloat32toFloat16PerformanceScalar) ||
(lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32) !=
lookup(capabilities.operandPerformance, V1_3::OperandType::FLOAT32))) {
return false;
}
return isQuantized8PerformanceConsistentWithP(capabilities);
}
bool compliantWithV1_2(const V1_0::Capabilities&) {
return true;
}
bool compliantWithV1_2(const V1_1::Capabilities&) {
return true;
}
bool compliantWithV1_2(const V1_2::Capabilities&) {
return true;
}
bool compliantWithV1_2(const V1_3::Capabilities&) {
return true;
}
bool compliantWithV1_3(const V1_0::Capabilities&) {
return true;
}
bool compliantWithV1_3(const V1_1::Capabilities&) {
return true;
}
bool compliantWithV1_3(const V1_2::Capabilities&) {
return true;
}
bool compliantWithV1_3(const V1_3::Capabilities&) {
return true;
}
V1_0::ErrorStatus convertToV1_0(V1_0::ErrorStatus status) {
return status;
}
V1_0::ErrorStatus convertToV1_0(V1_3::ErrorStatus status) {
switch (status) {
case V1_3::ErrorStatus::NONE:
return V1_0::ErrorStatus::NONE;
case V1_3::ErrorStatus::DEVICE_UNAVAILABLE:
return V1_0::ErrorStatus::DEVICE_UNAVAILABLE;
case V1_3::ErrorStatus::GENERAL_FAILURE:
return V1_0::ErrorStatus::GENERAL_FAILURE;
case V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
return V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
case V1_3::ErrorStatus::INVALID_ARGUMENT:
return V1_0::ErrorStatus::INVALID_ARGUMENT;
case V1_3::ErrorStatus::MISSED_DEADLINE_TRANSIENT:
return V1_0::ErrorStatus::GENERAL_FAILURE;
case V1_3::ErrorStatus::MISSED_DEADLINE_PERSISTENT:
return V1_0::ErrorStatus::GENERAL_FAILURE;
case V1_3::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT:
return V1_0::ErrorStatus::GENERAL_FAILURE;
case V1_3::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
return V1_0::ErrorStatus::GENERAL_FAILURE;
}
LOG(ERROR) << "Unknown ErrorStatus: " << toString(status) << " mapped to GENERAL_FAILURE";
return V1_0::ErrorStatus::GENERAL_FAILURE;
}
V1_3::ErrorStatus convertToV1_3(V1_0::ErrorStatus status) {
return static_cast<V1_3::ErrorStatus>(status);
}
V1_3::ErrorStatus convertToV1_3(V1_3::ErrorStatus status) {
return status;
}
static V1_0::OperationType uncheckedConvertToV1_0(V1_1::OperationType type) {
return static_cast<V1_0::OperationType>(type);
}
static V1_0::OperationType uncheckedConvertToV1_0(V1_2::OperationType type) {
return static_cast<V1_0::OperationType>(type);
}
V1_0::OperationType uncheckedConvertToV1_0(V1_3::OperationType type) {
return static_cast<V1_0::OperationType>(type);
}
static V1_1::OperationType convertToV1_1(V1_0::OperationType type) {
return static_cast<V1_1::OperationType>(type);
}
static V1_1::OperationType uncheckedConvertToV1_1(V1_2::OperationType type) {
return static_cast<V1_1::OperationType>(type);
}
V1_1::OperationType uncheckedConvertToV1_1(V1_3::OperationType type) {
return static_cast<V1_1::OperationType>(type);
}
static V1_2::OperationType convertToV1_2(V1_0::OperationType type) {
return static_cast<V1_2::OperationType>(type);
}
static V1_2::OperationType convertToV1_2(V1_1::OperationType type) {
return static_cast<V1_2::OperationType>(type);
}
V1_2::OperationType uncheckedConvertToV1_2(V1_3::OperationType type) {
return static_cast<V1_2::OperationType>(type);
}
static V1_3::OperationType convertToV1_3(V1_0::OperationType type) {
return static_cast<V1_3::OperationType>(type);
}
static V1_3::OperationType convertToV1_3(V1_1::OperationType type) {
return static_cast<V1_3::OperationType>(type);
}
static V1_3::OperationType convertToV1_3(V1_2::OperationType type) {
return static_cast<V1_3::OperationType>(type);
}
V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities) {
return capabilities;
}
V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities) {
if (!compliantWithV1_0(capabilities)) {
LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
<< " from V1_1::Capabilities to V1_0::Capabilities";
}
return {.float32Performance = capabilities.float32Performance,
.quantized8Performance = capabilities.quantized8Performance};
}
V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities) {
if (!compliantWithV1_0(capabilities)) {
LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
<< " from V1_2::Capabilities to V1_0::Capabilities";
}
return {.float32Performance =
lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32),
.quantized8Performance = lookup(capabilities.operandPerformance,
V1_2::OperandType::TENSOR_QUANT8_ASYMM)};
}
V1_0::Capabilities convertToV1_0(const V1_3::Capabilities& capabilities) {
if (!compliantWithV1_0(capabilities)) {
LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
<< " from V1_3::Capabilities to V1_0::Capabilities";
}
return {.float32Performance =
lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32),
.quantized8Performance = lookup(capabilities.operandPerformance,
V1_3::OperandType::TENSOR_QUANT8_ASYMM)};
}
V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities) {
return {.float32Performance = capabilities.float32Performance,
.quantized8Performance = capabilities.quantized8Performance,
.relaxedFloat32toFloat16Performance = capabilities.float32Performance};
}
V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities) {
return capabilities;
}
V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities) {
if (!compliantWithV1_1(capabilities)) {
LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
<< " from V1_2::Capabilities to V1_1::Capabilities";
}
return {.float32Performance =
lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32),
.quantized8Performance =
lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_QUANT8_ASYMM),
.relaxedFloat32toFloat16Performance =
capabilities.relaxedFloat32toFloat16PerformanceTensor};
}
V1_1::Capabilities convertToV1_1(const V1_3::Capabilities& capabilities) {
if (!compliantWithV1_1(capabilities)) {
LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities)
<< " from V1_3::Capabilities to V1_1::Capabilities";
}
return {.float32Performance =
lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32),
.quantized8Performance =
lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_QUANT8_ASYMM),
.relaxedFloat32toFloat16Performance =
capabilities.relaxedFloat32toFloat16PerformanceTensor};
}
V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities) {
V1_2::Capabilities ret = {
.relaxedFloat32toFloat16PerformanceScalar = capabilities.float32Performance,
.relaxedFloat32toFloat16PerformanceTensor = capabilities.float32Performance,
.operandPerformance =
makeQuantized8PerformanceConsistentWithP(capabilities.quantized8Performance)};
auto& opPerf = ret.operandPerformance;
opPerf.resize(opPerf.size() + 2);
opPerf[opPerf.size() - 2] = {V1_2::OperandType::TENSOR_FLOAT32,
capabilities.float32Performance};
opPerf[opPerf.size() - 1] = {V1_2::OperandType::FLOAT32, capabilities.float32Performance};
using OperandPerformance = V1_2::Capabilities::OperandPerformance;
std::sort(opPerf.begin(), opPerf.end(),
[](const OperandPerformance& a, const OperandPerformance& b) {
return a.type < b.type;
});
return ret;
}
V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities) {
V1_2::Capabilities ret = {.relaxedFloat32toFloat16PerformanceScalar =
capabilities.relaxedFloat32toFloat16Performance,
.relaxedFloat32toFloat16PerformanceTensor =
capabilities.relaxedFloat32toFloat16Performance,
.operandPerformance = makeQuantized8PerformanceConsistentWithP(
capabilities.quantized8Performance)};
auto& opPerf = ret.operandPerformance;
opPerf.resize(opPerf.size() + 2);
opPerf[opPerf.size() - 2] = {V1_2::OperandType::TENSOR_FLOAT32,
capabilities.float32Performance};
opPerf[opPerf.size() - 1] = {V1_2::OperandType::FLOAT32, capabilities.float32Performance};
using OperandPerformance = V1_2::Capabilities::OperandPerformance;
std::sort(opPerf.begin(), opPerf.end(),
[](const OperandPerformance& a, const OperandPerformance& b) {
return a.type < b.type;
});
return ret;
}
V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities) {
return capabilities;
}
V1_2::Capabilities convertToV1_2(const V1_3::Capabilities& capabilities) {
V1_2::Capabilities ret = {
.relaxedFloat32toFloat16PerformanceScalar =
capabilities.relaxedFloat32toFloat16PerformanceScalar,
.relaxedFloat32toFloat16PerformanceTensor =
capabilities.relaxedFloat32toFloat16PerformanceTensor,
};
const auto& inputOpPerf = capabilities.operandPerformance;
hardware::hidl_vec<V1_3::Capabilities::OperandPerformance> opPerfSupported;
opPerfSupported.resize(inputOpPerf.size());
auto last =
std::copy_if(inputOpPerf.begin(), inputOpPerf.end(), opPerfSupported.begin(),
[](V1_3::Capabilities::OperandPerformance opPerf) {
return validOperandType(static_cast<V1_2::OperandType>(opPerf.type));
});
opPerfSupported.resize(std::distance(opPerfSupported.begin(), last));
auto& convertedOpPerf = ret.operandPerformance;
convertedOpPerf.resize(opPerfSupported.size());
std::transform(opPerfSupported.begin(), opPerfSupported.end(), convertedOpPerf.begin(),
[](V1_3::Capabilities::OperandPerformance opPerf) {
return V1_2::Capabilities::OperandPerformance{
static_cast<V1_2::OperandType>(opPerf.type), opPerf.info};
});
return ret;
}
V1_3::Capabilities convertToV1_3(const V1_0::Capabilities& capabilities) {
return convertToV1_3(convertToV1_2(capabilities));
}
V1_3::Capabilities convertToV1_3(const V1_1::Capabilities& capabilities) {
return convertToV1_3(convertToV1_2(capabilities));
}
V1_3::Capabilities convertToV1_3(const V1_2::Capabilities& capabilities) {
V1_3::Capabilities ret = {
.relaxedFloat32toFloat16PerformanceScalar =
capabilities.relaxedFloat32toFloat16PerformanceScalar,
.relaxedFloat32toFloat16PerformanceTensor =
capabilities.relaxedFloat32toFloat16PerformanceTensor,
.ifPerformance = kNoPerformanceInfo,
.whilePerformance = kNoPerformanceInfo,
};
auto& opPerf = ret.operandPerformance;
opPerf.resize(capabilities.operandPerformance.size());
std::transform(capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
opPerf.begin(), [](V1_2::Capabilities::OperandPerformance opPerf) {
return V1_3::Capabilities::OperandPerformance{
static_cast<V1_3::OperandType>(opPerf.type), opPerf.info};
});
return ret;
}
V1_3::Capabilities convertToV1_3(const V1_3::Capabilities& capabilities) {
return capabilities;
}
static V1_0::Operation uncheckedConvertToV1_0(const V1_1::Operation& operation) {
return {.type = uncheckedConvertToV1_0(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_1::Operation convertToV1_1(const V1_0::Operation& operation) {
return {.type = convertToV1_1(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static hardware::hidl_vec<V1_0::Operation> uncheckedConvertToV1_0(
const hardware::hidl_vec<V1_1::Operation>& operations) {
hardware::hidl_vec<V1_0::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_1::Operation& operation) { return uncheckedConvertToV1_0(operation); });
return result;
}
static hardware::hidl_vec<V1_1::Operation> convertToV1_1(
const hardware::hidl_vec<V1_0::Operation>& operations) {
hardware::hidl_vec<V1_1::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_0::Operation& operation) { return convertToV1_1(operation); });
return result;
}
bool compliantWithV1_0(const V1_3::Operand& operand) {
return validOperandType(static_cast<V1_0::OperandType>(operand.type)) &&
(nonExtensionOperandTypeIsScalar(static_cast<int>(operand.type)) ||
operand.dimensions.size() != 0) &&
compliantWithV1_0(operand.lifetime);
}
bool compliantWithV1_2(const V1_3::Operand& operand) {
return validOperandType(static_cast<V1_2::OperandType>(operand.type)) &&
compliantWithV1_0(operand.lifetime);
}
bool compliantWithV1_3(const V1_3::Operand& operand) {
return true;
}
static bool compliantWith(HalVersion version, const V1_3::Model& model,
std::set<uint32_t>* noncompliantOperations) {
// A boolean vector indicating whether each pool is compliant with the target HAL version.
std::vector<bool> isPoolCompliant(model.pools.size(), false);
std::transform(
model.pools.begin(), model.pools.end(), isPoolCompliant.begin(),
[version](const hardware::hidl_memory& pool) { return validatePool(pool, version); });
// A boolean vector indicating whether each operand is compliant with the target HAL version.
std::vector<bool> isOperandCompliant(model.main.operands.size(), false);
std::transform(model.main.operands.begin(), model.main.operands.end(),
isOperandCompliant.begin(),
[&isPoolCompliant, version](const V1_3::Operand& op) {
bool is_operand_compliant = false;
switch (version) {
case HalVersion::UNKNOWN:
is_operand_compliant = false;
break;
case HalVersion::V1_0:
is_operand_compliant = compliantWithV1_0(op);
break;
case HalVersion::V1_1:
// There is no V1_1::Operand -- both V1_0::Model
// and V1_1::Model use V1_0::Operand.
is_operand_compliant = compliantWithV1_0(op);
break;
case HalVersion::V1_2:
is_operand_compliant = compliantWithV1_2(op);
break;
case HalVersion::V1_3:
is_operand_compliant = compliantWithV1_3(op);
break;
}
return is_operand_compliant &&
!(op.lifetime == V1_3::OperandLifeTime::CONSTANT_REFERENCE &&
!isPoolCompliant[op.location.poolIndex]);
});
auto allOperandsCompliant = [&isOperandCompliant](const hardware::hidl_vec<uint32_t>& indices) {
return std::all_of(
indices.begin(), indices.end(),
[&isOperandCompliant](const uint32_t ind) { return isOperandCompliant[ind]; });
};
auto localValidateOperation = [&model, version,
&allOperandsCompliant](const V1_3::Operation& op) {
if (!allOperandsCompliant(op.inputs) || !allOperandsCompliant(op.outputs)) return false;
int error = validateOperation(static_cast<int32_t>(op.type), op.inputs.size(),
op.inputs.size() > 0 ? op.inputs.data() : nullptr,
op.outputs.size(),
op.outputs.size() > 0 ? op.outputs.data() : nullptr,
uncheckedConvert(model.main.operands), version);
return error == ANEURALNETWORKS_NO_ERROR;
};
if (noncompliantOperations) {
CHECK(noncompliantOperations->empty());
for (uint32_t idx = 0; idx < model.main.operations.size(); ++idx) {
if (!localValidateOperation(model.main.operations[idx])) {
noncompliantOperations->insert(idx);
}
}
return noncompliantOperations->empty();
} else {
return std::all_of(model.main.operations.begin(), model.main.operations.end(),
localValidateOperation);
}
}
bool compliantWithV1_0(const V1_0::Model& model) {
return true;
}
bool compliantWithV1_0(const V1_1::Model& model) {
// In addition to new enumeration values being introduced in V1_1::Model, a
// new flag was introduced to indicate whether or not float32 data can be
// calculated using float16 units. This 'relaxComputationFloat32toFloat16'
// flag is not relevant in whether a V1_1::Model is compliant with a
// V1_0::Model because all 1.0 drivers require strict calculation by default
// in the P NN runtime. Even if fp16 calculations are allowed, they can
// still be computed by a strict fp32 driver.
auto operands = uncheckedConvert(convertToV1_3(model.operands));
return std::all_of(model.operations.begin(), model.operations.end(),
[&operands](const V1_1::Operation& op) {
int error = validateOperation(
static_cast<int32_t>(op.type), op.inputs.size(),
op.inputs.size() > 0 ? op.inputs.data() : nullptr,
op.outputs.size(),
op.outputs.size() > 0 ? op.outputs.data() : nullptr, operands,
HalVersion::V1_0);
return error == ANEURALNETWORKS_NO_ERROR;
});
}
bool compliantWithV1_0(const V1_2::Model& model, std::set<uint32_t>* noncompliantOperations) {
return compliantWith(HalVersion::V1_0, convertToV1_3(model), noncompliantOperations);
}
bool compliantWithV1_0(const V1_3::Model& model, std::set<uint32_t>* noncompliantOperations) {
return compliantWith(HalVersion::V1_0, model, noncompliantOperations);
}
bool compliantWithV1_1(const V1_0::Model&) {
return true;
}
bool compliantWithV1_1(const V1_1::Model&) {
return true;
}
bool compliantWithV1_1(const V1_2::Model& model, std::set<uint32_t>* noncompliantOperations) {
return compliantWith(HalVersion::V1_1, convertToV1_3(model), noncompliantOperations);
}
bool compliantWithV1_1(const V1_3::Model& model, std::set<uint32_t>* noncompliantOperations) {
return compliantWith(HalVersion::V1_1, model, noncompliantOperations);
}
bool compliantWithV1_2(const V1_0::Model&) {
return true;
}
bool compliantWithV1_2(const V1_1::Model&) {
return true;
}
bool compliantWithV1_2(const V1_2::Model&, std::set<uint32_t>* noncompliantOperations) {
return true;
}
bool compliantWithV1_2(const V1_3::Model& model, std::set<uint32_t>* noncompliantOperations) {
return compliantWith(HalVersion::V1_2, model, noncompliantOperations);
}
static V1_0::Operation uncheckedConvertToV1_0(const V1_2::Operation& operation) {
return {.type = uncheckedConvertToV1_0(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_0::Operation uncheckedConvertToV1_0(const V1_3::Operation& operation) {
return {.type = uncheckedConvertToV1_0(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_1::Operation uncheckedConvertToV1_1(const V1_2::Operation& operation) {
return {.type = uncheckedConvertToV1_1(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_1::Operation uncheckedConvertToV1_1(const V1_3::Operation& operation) {
return {.type = uncheckedConvertToV1_1(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_2::Operation convertToV1_2(const V1_0::Operation& operation) {
return {.type = convertToV1_2(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_2::Operation convertToV1_2(const V1_1::Operation& operation) {
return {.type = convertToV1_2(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_2::Operation uncheckedConvertToV1_2(const V1_3::Operation& operation) {
return {.type = uncheckedConvertToV1_2(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_3::Operation convertToV1_3(const V1_0::Operation& operation) {
return {.type = convertToV1_3(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_3::Operation convertToV1_3(const V1_1::Operation& operation) {
return {.type = convertToV1_3(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static V1_3::Operation convertToV1_3(const V1_2::Operation& operation) {
return {.type = convertToV1_3(operation.type),
.inputs = operation.inputs,
.outputs = operation.outputs};
}
static hardware::hidl_vec<V1_0::Operation> uncheckedConvertToV1_0(
const hardware::hidl_vec<V1_3::Operation>& operations) {
hardware::hidl_vec<V1_0::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_3::Operation& operation) { return uncheckedConvertToV1_0(operation); });
return result;
}
static hardware::hidl_vec<V1_0::Operation> uncheckedConvertToV1_0(
const hardware::hidl_vec<V1_2::Operation>& operations) {
hardware::hidl_vec<V1_0::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_2::Operation& operation) { return uncheckedConvertToV1_0(operation); });
return result;
}
static hardware::hidl_vec<V1_2::Operation> uncheckedConvertToV1_2(
const hardware::hidl_vec<V1_3::Operation>& operations) {
hardware::hidl_vec<V1_2::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_3::Operation& operation) { return uncheckedConvertToV1_2(operation); });
return result;
}
static hardware::hidl_vec<V1_1::Operation> uncheckedConvertToV1_1(
const hardware::hidl_vec<V1_2::Operation>& operations) {
hardware::hidl_vec<V1_1::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_2::Operation& operation) { return uncheckedConvertToV1_1(operation); });
return result;
}
static hardware::hidl_vec<V1_1::Operation> uncheckedConvertToV1_1(
const hardware::hidl_vec<V1_3::Operation>& operations) {
hardware::hidl_vec<V1_1::Operation> result(operations.size());
std::transform(
operations.begin(), operations.end(), result.begin(),
[](const V1_3::Operation& operation) { return uncheckedConvertToV1_1(operation); });
return result;
}
static hardware::hidl_vec<V1_2::Operation> convertToV1_2(
const hardware::hidl_vec<V1_0::Operation>& operations) {
hardware::hidl_vec<V1_2::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_0::Operation& operation) { return convertToV1_2(operation); });
return result;
}
static hardware::hidl_vec<V1_2::Operation> convertToV1_2(
const hardware::hidl_vec<V1_1::Operation>& operations) {
hardware::hidl_vec<V1_2::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_1::Operation& operation) { return convertToV1_2(operation); });
return result;
}
static hardware::hidl_vec<V1_3::Operation> convertToV1_3(
const hardware::hidl_vec<V1_0::Operation>& operations) {
hardware::hidl_vec<V1_3::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_0::Operation& operation) { return convertToV1_3(operation); });
return result;
}
static hardware::hidl_vec<V1_3::Operation> convertToV1_3(
const hardware::hidl_vec<V1_1::Operation>& operations) {
hardware::hidl_vec<V1_3::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_1::Operation& operation) { return convertToV1_3(operation); });
return result;
}
static hardware::hidl_vec<V1_3::Operation> convertToV1_3(
const hardware::hidl_vec<V1_2::Operation>& operations) {
hardware::hidl_vec<V1_3::Operation> result(operations.size());
std::transform(operations.begin(), operations.end(), result.begin(),
[](const V1_2::Operation& operation) { return convertToV1_3(operation); });
return result;
}
static bool compliantWithV1_0(const V1_2::OperandType& operandType) {
return validOperandType(static_cast<V1_0::OperandType>(operandType));
}
static bool compliantWithV1_0(const V1_3::OperandType& operandType) {
return validOperandType(static_cast<V1_0::OperandType>(operandType));
}
static bool compliantWithV1_2(const V1_3::OperandType& operandType) {
return validOperandType(static_cast<V1_2::OperandType>(operandType));
}
V1_0::OperandType convertToV1_0(const V1_2::OperandType& operandType) {
if (!compliantWithV1_0(operandType)) {
LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType)
<< " from V1_2::OperandType to V1_0::OperandType";
}
return static_cast<V1_0::OperandType>(operandType);
}
V1_2::OperandType convertToV1_2(const V1_0::OperandType& operandType) {
return static_cast<V1_2::OperandType>(operandType);
}
V1_2::OperandType convertToV1_2(const V1_3::OperandType& operandType) {
if (!compliantWithV1_2(operandType)) {
LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType)
<< " from V1_3::OperandType to V1_2::OperandType";
}
return static_cast<V1_2::OperandType>(operandType);
}
V1_0::OperandType convertToV1_0(const V1_3::OperandType& operandType) {
if (!compliantWithV1_0(operandType)) {
LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType)
<< " from V1_3::Operand to V1_0::Operand";
}
return static_cast<V1_0::OperandType>(operandType);
}
bool compliantWithV1_0(V1_0::OperandLifeTime lifetime) {
return true;
}
bool compliantWithV1_0(V1_3::OperandLifeTime lifetime) {
return lifetime != V1_3::OperandLifeTime::SUBGRAPH;
}
bool compliantWithV1_3(V1_0::OperandLifeTime lifetime) {
return true;
}
bool compliantWithV1_3(V1_3::OperandLifeTime lifetime) {
return true;
}
V1_0::OperandLifeTime convertToV1_0(V1_0::OperandLifeTime lifetime) {
return lifetime;
}
V1_0::OperandLifeTime convertToV1_0(V1_3::OperandLifeTime lifetime) {
if (!compliantWithV1_0(lifetime)) {
LOG(ERROR) << "Upcasting non-compliant lifetime " << toString(lifetime)
<< " from V1_3 to V1_0";
}
return static_cast<V1_0::OperandLifeTime>(lifetime);
}
V1_3::OperandLifeTime convertToV1_3(V1_0::OperandLifeTime lifetime) {
return static_cast<V1_3::OperandLifeTime>(lifetime);
}
V1_3::OperandLifeTime convertToV1_3(V1_3::OperandLifeTime lifetime) {
return lifetime;
}
V1_0::Operand convertToV1_0(const V1_2::Operand& operand) {
return {.type = convertToV1_0(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = convertToV1_0(operand.lifetime),
.location = operand.location};
}
V1_0::Operand convertToV1_0(const V1_3::Operand& operand) {
return {.type = convertToV1_0(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = convertToV1_0(operand.lifetime),
.location = operand.location};
}
V1_2::Operand convertToV1_2(const V1_0::Operand& operand) {
return {.type = convertToV1_2(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = operand.lifetime,
.location = operand.location};
}
V1_2::Operand convertToV1_2(const V1_3::Operand& operand) {
return {.type = convertToV1_2(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = static_cast<V1_0::OperandLifeTime>(operand.lifetime),
.location = operand.location,
.extraParams = operand.extraParams};
}
V1_3::Operand convertToV1_3(const V1_0::Operand& operand) {
return {.type = static_cast<V1_3::OperandType>(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = convertToV1_3(operand.lifetime),
.location = operand.location};
}
V1_3::Operand convertToV1_3(const V1_2::Operand& operand) {
return {.type = static_cast<V1_3::OperandType>(operand.type),
.dimensions = operand.dimensions,
.numberOfConsumers = operand.numberOfConsumers,
.scale = operand.scale,
.zeroPoint = operand.zeroPoint,
.lifetime = convertToV1_3(operand.lifetime),
.location = operand.location,
.extraParams = operand.extraParams};
}
V1_3::Operand convertToV1_3(const V1_3::Operand& operand) {
return operand;
}
hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_0::Operand>& operands) {
return operands;
}
hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_2::Operand>& operands) {
hardware::hidl_vec<V1_0::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_2::Operand& operand) { return convertToV1_0(operand); });
return result;
}
hardware::hidl_vec<V1_0::Operand> convertToV1_0(const hardware::hidl_vec<V1_3::Operand>& operands) {
hardware::hidl_vec<V1_0::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_3::Operand& operand) { return convertToV1_0(operand); });
return result;
}
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_0::Operand>& operands) {
hardware::hidl_vec<V1_2::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_0::Operand& operand) { return convertToV1_2(operand); });
return result;
}
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_2::Operand>& operands) {
return operands;
}
hardware::hidl_vec<V1_2::Operand> convertToV1_2(const hardware::hidl_vec<V1_3::Operand>& operands) {
hardware::hidl_vec<V1_2::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_3::Operand& operand) { return convertToV1_2(operand); });
return result;
}
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_0::Operand>& operands) {
hardware::hidl_vec<V1_3::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_0::Operand& operand) { return convertToV1_3(operand); });
return result;
}
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_2::Operand>& operands) {
hardware::hidl_vec<V1_3::Operand> result(operands.size());
std::transform(operands.begin(), operands.end(), result.begin(),
[](const V1_2::Operand& operand) { return convertToV1_3(operand); });
return result;
}
hardware::hidl_vec<V1_3::Operand> convertToV1_3(const hardware::hidl_vec<V1_3::Operand>& operands) {
return operands;
}
V1_0::Model convertToV1_0(const V1_0::Model& model) {
return model;
}
V1_0::Model convertToV1_0(const V1_1::Model& model) {
if (!compliantWithV1_0(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_1::Model to V1_0::Model";
}
return {.operands = model.operands,
.operations = uncheckedConvertToV1_0(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools};
}
V1_0::Model convertToV1_0(const V1_2::Model& model) {
if (!compliantWithV1_0(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_2::Model to V1_0::Model";
}
return {.operands = convertToV1_0(model.operands),
.operations = uncheckedConvertToV1_0(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools};
}
V1_0::Model convertToV1_0(const V1_3::Model& model) {
if (!compliantWithV1_0(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_3::Model to V1_0::Model";
}
return {.operands = convertToV1_0(model.main.operands),
.operations = uncheckedConvertToV1_0(model.main.operations),
.inputIndexes = model.main.inputIndexes,
.outputIndexes = model.main.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools};
}
V1_1::Model convertToV1_1(const V1_0::Model& model) {
return {.operands = model.operands,
.operations = convertToV1_1(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = false};
}
V1_1::Model convertToV1_1(const V1_1::Model& model) {
return model;
}
V1_1::Model convertToV1_1(const V1_2::Model& model) {
if (!compliantWithV1_1(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_2::Model to V1_1::Model";
}
return {.operands = convertToV1_0(model.operands), // Operands in 1.1 and 1.0 are identical.
.operations = uncheckedConvertToV1_1(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
}
V1_1::Model convertToV1_1(const V1_3::Model& model) {
if (!compliantWithV1_1(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_3::Model to V1_1::Model";
}
return {// Operands in 1.1 and 1.0 are identical.
.operands = convertToV1_0(model.main.operands),
.operations = uncheckedConvertToV1_1(model.main.operations),
.inputIndexes = model.main.inputIndexes,
.outputIndexes = model.main.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
}
V1_2::Model convertToV1_2(const V1_0::Model& model) {
return {.operands = convertToV1_2(model.operands),
.operations = convertToV1_2(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = false};
}
V1_2::Model convertToV1_2(const V1_1::Model& model) {
return {.operands = convertToV1_2(model.operands),
.operations = convertToV1_2(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
}
V1_2::Model convertToV1_2(const V1_2::Model& model) {
return model;
}
V1_2::Model convertToV1_2(const V1_3::Model& model) {
if (!compliantWithV1_2(model)) {
LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model))
<< " from V1_3::Model to V1_2::Model";
}
return {.operands = convertToV1_2(model.main.operands),
.operations = uncheckedConvertToV1_2(model.main.operations),
.inputIndexes = model.main.inputIndexes,
.outputIndexes = model.main.outputIndexes,
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
.extensionNameToPrefix = model.extensionNameToPrefix};
}
V1_3::Model convertToV1_3(const V1_0::Model& model) {
return {.main = {.operands = convertToV1_3(model.operands),
.operations = convertToV1_3(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes},
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = false};
}
V1_3::Model convertToV1_3(const V1_1::Model& model) {
return {.main = {.operands = convertToV1_3(model.operands),
.operations = convertToV1_3(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes},
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16};
}
V1_3::Model convertToV1_3(const V1_2::Model& model) {
return {.main = {.operands = convertToV1_3(model.operands),
.operations = convertToV1_3(model.operations),
.inputIndexes = model.inputIndexes,
.outputIndexes = model.outputIndexes},
.operandValues = model.operandValues,
.pools = model.pools,
.relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
.extensionNameToPrefix = model.extensionNameToPrefix};
}
V1_3::Model convertToV1_3(const V1_3::Model& model) {
return model;
}
bool compliantWithV1_0(const V1_0::Request& request) {
return true;
}
bool compliantWithV1_0(const V1_3::Request& request) {
return std::all_of(request.pools.begin(), request.pools.end(), [](const auto& pool) {
if (pool.getDiscriminator() != V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory) {
return false;
}
const auto& name = pool.hidlMemory().name();
return name == "ashmem" || name == "mmap_fd";
});
}
bool compliantWithV1_2(const V1_3::Request& request) {
return std::all_of(request.pools.begin(), request.pools.end(), [](const auto& pool) {
if (pool.getDiscriminator() != V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory) {
return false;
}
const auto& name = pool.hidlMemory().name();
return name == "ashmem" || name == "mmap_fd" || name == "hardware_buffer_blob" ||
name == "hardware_buffer";
});
}
static hardware::hidl_memory convertToV1_0(const V1_3::Request::MemoryPool& pool) {
switch (pool.getDiscriminator()) {
case V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory:
return pool.hidlMemory();
case V1_3::Request::MemoryPool::hidl_discriminator::token:
return hardware::hidl_memory{};
}
}
static V1_3::Request::MemoryPool convertToV1_3(const hardware::hidl_memory& pool) {
V1_3::Request::MemoryPool ret;
ret.hidlMemory(pool);
return ret;
}
V1_0::Request convertToV1_0(const V1_0::Request& request) {
return request;
}
static V1_0::Request uncheckedConvertToV1_0(const V1_3::Request& request) {
hardware::hidl_vec<hardware::hidl_memory> pools(request.pools.size());
std::transform(request.pools.begin(), request.pools.end(), pools.begin(),
[](const auto& pool) { return convertToV1_0(pool); });
return {.inputs = request.inputs, .outputs = request.outputs, .pools = std::move(pools)};
}
V1_0::Request convertToV1_0(const V1_3::Request& request) {
if (!compliantWithV1_0(request)) {
LOG(ERROR) << "Upcasting non-compliant request " << SHOW_IF_DEBUG(toString(request))
<< " from V1_3::Request to V1_0::Request of version 1.0";
}
return uncheckedConvertToV1_0(request);
}
V1_0::Request convertToV1_2(const V1_3::Request& request) {
if (!compliantWithV1_2(request)) {
LOG(ERROR) << "Upcasting non-compliant request " << SHOW_IF_DEBUG(toString(request))
<< " from V1_3::Request to V1_0::Request of version 1.2";
}
return uncheckedConvertToV1_0(request);
}
V1_3::Request convertToV1_3(const V1_0::Request& request) {
hardware::hidl_vec<V1_3::Request::MemoryPool> pools(request.pools.size());
std::transform(request.pools.begin(), request.pools.end(), pools.begin(),
[](const auto& pool) { return convertToV1_3(pool); });
return {.inputs = request.inputs, .outputs = request.outputs, .pools = std::move(pools)};
}
V1_3::Request convertToV1_3(const V1_3::Request& request) {
return request;
}
template <typename Type>
static Type handleError(GeneralResult<Type> result) {
CHECK(result.has_value()) << "Unhandled error (" << result.error().code
<< "): " << result.error().message;
return std::move(result).value();
}
ErrorStatus uncheckedConvert(V1_0::ErrorStatus status) {
return handleError(convert(status));
}
ErrorStatus uncheckedConvert(V1_3::ErrorStatus status) {
return handleError(convert(status));
}
OperandType uncheckedConvert(V1_3::OperandType operandType) {
return handleError(convert(operandType));
}
OperationType uncheckedConvert(V1_3::OperationType operandType) {
return handleError(convert(operandType));
}
Operand::LifeTime uncheckedConvert(V1_3::OperandLifeTime lifetime) {
return handleError(convert(lifetime));
}
MeasureTiming uncheckedConvert(V1_2::MeasureTiming measure) {
return handleError(convert(measure));
}
DataLocation uncheckedConvert(const V1_0::DataLocation& location) {
return handleError(convert(location));
}
Operand uncheckedConvert(const V1_3::Operand& operand) {
return handleError(convert(operand));
}
Operand::ExtraParams uncheckedConvert(const V1_2::Operand::ExtraParams& params) {
return handleError(convert(params));
}
Operand::SymmPerChannelQuantParams uncheckedConvert(const V1_2::SymmPerChannelQuantParams& params) {
return handleError(convert(params));
}
Operand::ExtensionParams uncheckedConvert(const hardware::hidl_vec<uint8_t>& params) {
return params;
}
Operation uncheckedConvert(const V1_3::Operation& operation) {
return handleError(convert(operation));
}
template <typename CanonicalType, typename HalType>
static std::vector<CanonicalType> convertVec(const hardware::hidl_vec<HalType>& items) {
std::vector<CanonicalType> result(items.size());
std::transform(items.begin(), items.end(), result.begin(),
[](const HalType& item) { return uncheckedConvert(item); });
return result;
}
Model uncheckedConvert(const V1_3::Model& model) {
return handleError(convert(model));
}
Model::Subgraph uncheckedConvert(const V1_3::Subgraph& subgraph) {
return handleError(convert(subgraph));
}
Model::ExtensionNameAndPrefix uncheckedConvert(const V1_2::Model::ExtensionNameAndPrefix& x) {
return handleError(convert(x));
}
Request uncheckedConvert(const V1_3::Request& request) {
return handleError(convert(request));
}
Request::Argument uncheckedConvert(const V1_0::RequestArgument& requestArgument) {
return handleError(convert(requestArgument));
}
Request::MemoryPool uncheckedConvert(const V1_3::Request::MemoryPool& memoryPool) {
return handleError(convert(memoryPool));
}
OutputShape uncheckedConvert(const V1_2::OutputShape& outputShape) {
return handleError(convert(outputShape));
}
std::vector<OutputShape> uncheckedConvert(
const hardware::hidl_vec<V1_2::OutputShape>& outputShapes) {
return convertVec<OutputShape>(outputShapes);
}
Capabilities uncheckedConvert(const V1_3::Capabilities& capabilities) {
return handleError(convert(capabilities));
}
Capabilities::OperandPerformance uncheckedConvert(
const V1_3::Capabilities::OperandPerformance& operandPerformance) {
return handleError(convert(operandPerformance));
}
Capabilities::PerformanceInfo uncheckedConvert(const V1_0::PerformanceInfo& performanceInfo) {
return handleError(convert(performanceInfo));
}
Extension uncheckedConvert(const V1_2::Extension& extension) {
return handleError(convert(extension));
}
std::vector<Extension> uncheckedConvert(const hardware::hidl_vec<V1_2::Extension>& extensions) {
return convertVec<Extension>(extensions);
}
Extension::OperandTypeInformation uncheckedConvert(
const V1_2::Extension::OperandTypeInformation& info) {
return handleError(convert(info));
}
OptionalTimeoutDuration uncheckedConvert(const V1_3::OptionalTimeoutDuration& timeoutDuration) {
return handleError(convert(timeoutDuration));
}
Timing uncheckedConvert(const V1_2::Timing& timing) {
return handleError(convert(timing));
}
V1_0::ErrorStatus convertToV1_0(ErrorStatus status) {
return static_cast<V1_0::ErrorStatus>(static_cast<int>(status));
}
V1_3::ErrorStatus convertToV1_3(ErrorStatus status) {
return handleError(V1_3::utils::convert(status));
}
V1_3::OperandType convertToV1_3(OperandType operandType) {
return handleError(V1_3::utils::convert(operandType));
}
V1_3::OperationType convertToV1_3(OperationType operandType) {
return handleError(V1_3::utils::convert(operandType));
}
V1_3::OperandLifeTime convertToV1_3(Operand::LifeTime lifetime) {
return handleError(V1_3::utils::convert(lifetime));
}
V1_1::ExecutionPreference convertToV1_1(ExecutionPreference preference) {
return handleError(V1_1::utils::convert(preference));
}
V1_3::Priority convertToV1_3(Priority priority) {
return handleError(V1_3::utils::convert(priority));
}
V1_2::MeasureTiming convertToV1_2(MeasureTiming measure) {
return handleError(V1_2::utils::convert(measure));
}
V1_0::DataLocation convertToV1_0(const DataLocation& location) {
return handleError(V1_0::utils::convert(location));
}
V1_3::Operand convertToV1_3(const Operand& operand) {
return handleError(V1_3::utils::convert(operand));
}
V1_2::Operand::ExtraParams convertToV1_2(const Operand::ExtraParams& params) {
return handleError(V1_2::utils::convert(params));
}
V1_2::SymmPerChannelQuantParams convertToV1_2(const Operand::SymmPerChannelQuantParams& params) {
return handleError(V1_2::utils::convert(params));
}
hardware::hidl_vec<uint8_t> uncheckedConvert(const Operand::ExtensionParams& params) {
return params;
}
V1_3::Operation convertToV1_3(const Operation& operation) {
return handleError(V1_3::utils::convert(operation));
}
template <typename HalType, typename CanonicalType>
static hardware::hidl_vec<HalType> convertVecToV1_0(const std::vector<CanonicalType>& items) {
hardware::hidl_vec<HalType> result(items.size());
std::transform(items.begin(), items.end(), result.begin(),
[](const CanonicalType& item) { return convertToV1_0(item); });
return result;
}
template <typename HalType, typename CanonicalType>
static hardware::hidl_vec<HalType> convertVecToV1_2(const std::vector<CanonicalType>& items) {
hardware::hidl_vec<HalType> result(items.size());
std::transform(items.begin(), items.end(), result.begin(),
[](const CanonicalType& item) { return convertToV1_2(item); });
return result;
}
template <typename HalType, typename CanonicalType>
static hardware::hidl_vec<HalType> convertVecToV1_3(const std::vector<CanonicalType>& items) {
hardware::hidl_vec<HalType> result(items.size());
std::transform(items.begin(), items.end(), result.begin(),
[](const CanonicalType& item) { return convertToV1_3(item); });
return result;
}
V1_2::OutputShape convertToV1_2(const OutputShape& outputShape) {
return handleError(V1_2::utils::convert(outputShape));
}
hardware::hidl_vec<V1_2::OutputShape> convertToV1_2(const std::vector<OutputShape>& outputShapes) {
return convertVecToV1_2<V1_2::OutputShape>(outputShapes);
}
V1_3::Model convertToV1_3(const Model& model) {
return handleError(V1_3::utils::convert(model));
}
V1_3::Subgraph convertToV1_3(const Model::Subgraph& subgraph) {
return handleError(V1_3::utils::convert(subgraph));
}
V1_2::Model::ExtensionNameAndPrefix convertToV1_2(const Model::ExtensionNameAndPrefix& x) {
return handleError(V1_2::utils::convert(x));
}
V1_3::Request convertToV1_3(const Request& request) {
return handleError(V1_3::utils::convert(request));
}
V1_0::RequestArgument convertToV1_0(const Request::Argument& requestArgument) {
return handleError(V1_0::utils::convert(requestArgument));
}
V1_3::Request::MemoryPool convertToV1_3(const Request::MemoryPool& memoryPool) {
return handleError(V1_3::utils::convert(memoryPool));
}
std::vector<Request::MemoryPool> uncheckedConvert(
const hardware::hidl_vec<V1_3::Request::MemoryPool>& memoryPools) {
return convertVec<Request::MemoryPool>(memoryPools);
}
V1_3::OptionalTimePoint convertToV1_3(const OptionalTimePoint& timePoint) {
return handleError(V1_3::utils::convert(timePoint));
}
V1_3::OptionalTimeoutDuration convertToV1_3(const OptionalTimeoutDuration& timeoutDuration) {
return handleError(V1_3::utils::convert(timeoutDuration));
}
V1_2::Timing convertToV1_2(const Timing& timing) {
return handleError(V1_2::utils::convert(timing));
}
V1_3::BufferRole convertToV1_3(const BufferRole& bufferRole) {
return handleError(V1_3::utils::convert(bufferRole));
}
hardware::hidl_vec<V1_3::BufferRole> convertToV1_3(const std::vector<BufferRole>& bufferRoles) {
return convertVecToV1_3<V1_3::BufferRole>(bufferRoles);
}
hardware::hidl_vec<uint8_t> convertToV1_0(const Model::OperandValues& operandValues) {
return handleError(V1_0::utils::convert(operandValues));
}
hardware::hidl_memory convertToV1_0(const Memory& memory) {
return handleError(V1_0::utils::convert(memory));
}
Memory uncheckedConvert(const hardware::hidl_memory& memory) {
return handleError(convert(memory));
}
hardware::hidl_vec<hardware::hidl_memory> convertToV1_0(const std::vector<Memory>& memories) {
return convertVecToV1_0<hardware::hidl_memory>(memories);
}
std::vector<Memory> uncheckedConvert(const hardware::hidl_vec<hardware::hidl_memory>& memories) {
return convertVec<Memory>(memories);
}
std::vector<Model::Subgraph> uncheckedConvert(const hardware::hidl_vec<V1_3::Subgraph>& subgraphs) {
return convertVec<Model::Subgraph>(subgraphs);
}
std::vector<Operand> uncheckedConvert(const hardware::hidl_vec<V1_3::Operand>& operands) {
return convertVec<Operand>(operands);
}
} // namespace nn
} // namespace android