blob: b2ce63ac0bb83adc7a24f11a1aa4437fc98528ae [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 "OperationsUtils"
#include "OperationsUtils.h"
#include "Operations.h"
#include "Utils.h"
#include <cmath>
namespace android {
namespace nn {
bool SameShape(const Shape& in1, const Shape& in2) {
if (in1.type != in2.type || in1.dimensions.size() != in2.dimensions.size()) {
return false;
}
for (size_t i = 0; i < in1.dimensions.size(); i++) {
if (in1.dimensions[i] != in2.dimensions[i]) {
return false;
}
}
return true;
}
bool SetShape(const Shape& in, Shape* out) {
if (in.type != out->type || in.dimensions.size() != out->dimensions.size()) {
return false;
}
out->dimensions = in.dimensions;
return true;
}
uint32_t getNumberOfElements(const Shape& shape) {
uint32_t count = 1;
for (size_t i = 0; i < shape.dimensions.size(); i++) {
count *= shape.dimensions[i];
}
return count;
}
uint32_t getNumberOfDimensions(const Shape& shape) {
return shape.dimensions.size();
}
uint32_t getSizeOfDimension(const Shape& shape, uint32_t dimensionIdx) {
if (dimensionIdx >= shape.dimensions.size()) {
// TODO, log the error
return 0;
}
return shape.dimensions[dimensionIdx];
}
void QuantizeMultiplierSmallerThanOne(double double_multiplier,
int32_t* quantized_multiplier,
int32_t* right_shift) {
CHECK(double_multiplier >= 0.);
CHECK(double_multiplier < 1.);
if (double_multiplier == 0.) {
*quantized_multiplier = 0;
*right_shift = 0;
return;
}
CHECK(double_multiplier > 0.);
const double q = std::frexp(double_multiplier, right_shift);
*right_shift *= -1;
int64_t q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
CHECK(q_fixed <= (1ll << 31));
if (q_fixed == (1ll << 31)) {
q_fixed /= 2;
--*right_shift;
}
CHECK_GE(*right_shift, 0);
CHECK_LE(q_fixed, std::numeric_limits<int32_t>::max());
*quantized_multiplier = static_cast<int32_t>(q_fixed);
}
void QuantizeMultiplierGreaterThanOne(double double_multiplier,
int32_t* quantized_multiplier,
int* left_shift) {
CHECK(double_multiplier > 1.);
const double q = std::frexp(double_multiplier, left_shift);
int64_t q_fixed = static_cast<int64_t>(std::round(q * (1ll << 31)));
CHECK(q_fixed <= (1ll << 31));
if (q_fixed == (1ll << 31)) {
q_fixed /= 2;
++*left_shift;
}
CHECK_GE(*left_shift, 0);
CHECK_LE(q_fixed, std::numeric_limits<int32_t>::max());
*quantized_multiplier = static_cast<int32_t>(q_fixed);
}
void GetQuantizedConvolutionMultipler(const Shape& inputShape,
const Shape& filterShape,
const Shape& biasShape,
const Shape& outputShape,
float* multiplier) {
const float input_product_scale = inputShape.scale * filterShape.scale;
const float bias_scale = biasShape.scale;
const float output_scale = outputShape.scale;
// The following conditions must be guaranteed by the training pipeline.
CHECK(std::abs(input_product_scale - bias_scale) <=
1e-6 * std::min(input_product_scale, bias_scale));
CHECK(input_product_scale >= 0);
CHECK(input_product_scale < output_scale);
*multiplier = input_product_scale / output_scale;
}
void CalculateActivationRangeUint8(int32_t activation,
const Shape& outputShape,
int32_t* act_min,
int32_t* act_max) {
const int32_t qmin = std::numeric_limits<uint8_t>::min();
const int32_t qmax = std::numeric_limits<uint8_t>::max();
const auto scale = outputShape.scale;
const auto zero_point = outputShape.offset;
auto quantize = [scale, zero_point](float f) {
return zero_point + static_cast<int32_t>(std::round(f / scale));
};
if (activation == kActivationRelu) {
*act_min = std::max(qmin, quantize(0.0));
*act_max = qmax;
} else if (activation == kActivationRelu6) {
*act_min = std::max(qmin, quantize(0.0));
*act_max = std::min(qmax, quantize(6.0));
} else if (activation == kActivationRelu1) {
*act_min = std::max(qmin, quantize(-1.0));
*act_max = std::min(qmax, quantize(1.0));
} else {
*act_min = qmin;
*act_max = qmax;
}
}
int32_t CalculateInputRadius(int input_integer_bits, int input_left_shift) {
const double max_input_rescaled = 1.0 * ((1 << input_integer_bits) - 1) *
(1ll << (31 - input_integer_bits)) /
(1ll << input_left_shift);
// Tighten bound using floor. Suppose that we could use the exact value.
// After scaling the difference, the result would be at the maximum. Thus we
// must ensure that our value has lower magnitude.
return static_cast<int32_t>(std::floor(max_input_rescaled));
}
} // namespace nn
} // namespace android