Source code for qkeras.qtools.quantized_operators.accumulator_impl

# Copyright 2019 Google LLC
#
#
# 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.
# ==============================================================================
"""Accumulator operation implementation."""


import abc

import keras.ops.numpy as knp
from absl import logging

from qkeras.qtools.quantized_operators import multiplier_impl, quantizer_impl


[docs] def po2_to_qbits(quantizer: quantizer_impl.IQuantizer): """convert po2 type to qbits type.""" (min_exp, max_exp) = quantizer.get_min_max_exp() # min_exp is number of bits needed on the right in qbits # max_exp is number of bits needed on the left in qbits unsigned_bits = min_exp + max_exp int_bits = max_exp sign_bit = quantizer.is_signed bits = sign_bit + unsigned_bits return (int(bits), int(int_bits))
[docs] class IAccumulator(abc.ABC): """abstract class for accumulator."""
[docs] @staticmethod @abc.abstractmethod def implemented_as(): pass
[docs] class FloatingPointAccumulator(IAccumulator): """class for floating point accumulator.""" def __init__(self, multiplier: multiplier_impl.IMultiplier): super().__init__() self.multiplier = multiplier self.output = quantizer_impl.FloatingPoint(bits=self.multiplier.output.bits) self.output.bits = self.multiplier.output.bits self.output.int_bits = -1 self.output.is_signed = self.multiplier.output.is_signed self.output.is_floating_point = True self.output.op_type = "accumulator"
[docs] @staticmethod def implemented_as(): return "add"
[docs] class FixedPointAccumulator(IAccumulator): """class for fixed point accumulator.""" def __init__( self, kernel_shape, multiplier: multiplier_impl.IMultiplier, use_bias=True ): super().__init__() if len(kernel_shape) not in ( 2, 4, ): logging.fatal( "unsupported kernel shape, " "it is neither a dense kernel of length 2," " nor a convolution kernel of length 4" ) kernel_shape_excluding_output_dim = kernel_shape[:-1] kernel_add_ops = knp.prod(kernel_shape_excluding_output_dim) # bias are associate with filters; each filter adds 1 bias bias_add = 1 if use_bias else 0 add_ops = kernel_add_ops + bias_add self.log_add_ops = int(knp.ceil(knp.log2(add_ops))) self.multiplier = multiplier self.output = quantizer_impl.QuantizedBits() self.output.bits = self.log_add_ops + self.multiplier.output.bits self.output.int_bits = self.log_add_ops + self.multiplier.output.int_bits self.output.is_signed = self.multiplier.output.is_signed self.output.op_type = "accumulator" assert not self.multiplier.output.is_floating_point self.output.is_floating_point = False
[docs] @staticmethod def implemented_as(): return "add"
[docs] class Po2Accumulator(FixedPointAccumulator): """accumulator for po2.""" # multiplier is po2. multiplier output needs to convert # to Fixedpoint before Accumulator. def __init__( self, kernel_shape, multiplier: multiplier_impl.IMultiplier, use_bias=True ): super().__init__(kernel_shape, multiplier, use_bias) assert multiplier.output.is_po2 # convert multiplier output from po2 to quantized_bits (bits_from_po2multiplier, int_bits_from_po2multiplier) = po2_to_qbits( multiplier.output ) self.output.bits = self.log_add_ops + int(bits_from_po2multiplier) self.output.int_bits = self.log_add_ops + int(int_bits_from_po2multiplier) self.output.op_type = "accumulator"
[docs] @staticmethod def implemented_as(): return "add"