# Path: qkeras/quantizers.py
# def _get_integer_bits(min_value,
#                       max_value,
#                       bits=8,
#                       symmetric=False,
#                       keep_negative=False,
#                       is_clipping=True):
#   """Estimates the integer bit(number of bits to the left of the binary point)
#   satisfying the input argument constraints.
# 
#   Args:
#     min_value: A tensor object. Its elements are in float representing the
#       minimum values of ranges.
#     max_value: A tensor object. Its elements are in float representing the
#       maximum values of ranges.
#     bits: number of bits to perform quantization.
#     symmetric: boolean type. if true, it enforces negative and positive ranges
#       to be symmetric.
#     keep_negative: boolean type. if true, we do not clip negative numbers.
#     is_clipping: boolean type. if true, the min_value and max_value are clipped
#       to nearest powers-of-2.
# 
#   Returns:
#     integer_bits : number of bits to the left of the binary point.
#   """
#   # Max the min and max values positive if only using positive values
#   if not keep_negative:
#     min_value = K.maximum(min_value, 0)
#     max_value = K.maximum(max_value, 0)
# 
#   # The number of bits excluding the sign bit
#   unsigned_bits = bits - keep_negative
# 
#   # log2 of absolute min_value and max_value
#   min_value_log2 = K.log(K.abs(min_value)) / np.log(2.0)
#   max_value_log2 = K.log(K.abs(max_value)) / np.log(2.0)
# 
#   # Estimate integer_bits
#   if is_clipping:
#     min_int_bits = tf.math.round(
#         tf.where(min_value_log2 > 0, min_value_log2, 0))
#     max_int_bits = tf.math.round(
#         tf.where(max_value_log2 > 0, max_value_log2, 0))
#   else:
#     min_int_bits = tf.math.ceil(tf.where(min_value_log2 > 0, min_value_log2, 0))
#     max_int_bits = tf.math.ceil(tf.where(max_value_log2 > 0, max_value_log2, 0))
#     # Checks max_value is bounded by the maximum positive value of
#     # pow(2,integer_bits) - pow(2,-fractional_bits).
#     max_value_po2 = pow(2.0, max_int_bits) - pow(
#         2.0, K.minimum(max_int_bits - unsigned_bits, 0))
#     max_int_bits = tf.where(max_value <= max_value_po2, max_int_bits,
#                             max_int_bits + 1)
#     if symmetric:
#       # Checks min_value is bounded by the minimum negative value of
#       # - pow(2,integer_bits) + pow(2,-fractional_bits).
#       min_value_po2 = -pow(2.0, min_int_bits) + pow(
#           2.0, K.minimum(min_int_bits - unsigned_bits, 0))
#       min_int_bits = tf.where(min_value_po2 <= min_value, min_int_bits,
#                               min_int_bits + 1)
# 
#   # To cover both negative and positive ranges with integer_bits.
#   # (For keep_negative=False, min_int_bits is 0.)
#   integer_bits = tf.cast(K.maximum(min_int_bits, max_int_bits), dtype=tf.int32)
#   # It assumes that integer_bits cannot be greater than unsigned_bits
#   integer_bits = K.minimum(unsigned_bits, integer_bits)
# 
#   return integer_bits
# 
# Path: qkeras/quantizers.py
# def get_quantizer(identifier):
#   """Gets the quantizer.
# 
#   Args:
#     identifier: An quantizer, which could be dict, string, or callable function.
# 
#   Returns:
#     A quantizer class or quantization function from this file. For example,
#       Quantizer classes: quantized_bits, quantized_po2, quantized_relu_po2,
#       binary, stochastic_binary, ternary, stochastic_ternary, etc.
# 
#       Quantization functions: binary_sigmoid, hard_sigmoid, soft_sigmoid, etc.
# 
#   Raises:
#     ValueError: An error occurred when quantizer cannot be interpreted.
#   """
# 
#   if identifier is None:
#     return None
#   if isinstance(identifier, dict):
#     return deserialize_keras_object(
#         identifier, module_objects=globals(), printable_module_name="quantizer")
#   elif isinstance(identifier, six.string_types):
#     return safe_eval(identifier, globals())
#   elif callable(identifier):
#     return identifier
#   else:
#     raise ValueError("Could not interpret quantizer identifier: " +
#                      str(identifier))


import sys
import warnings
import numpy as np
import six
import tensorflow.compat.v2 as tf
import tensorflow.keras.backend as K
from tensorflow.keras import activations
from tensorflow.keras import constraints
from tensorflow.keras import initializers
from tensorflow.keras import regularizers
from tensorflow.keras.constraints import Constraint
from tensorflow.keras.initializers import Initializer
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Layer
from tensorflow.python.framework import smart_cond as tf_utils
from .quantizers import *
from .quantizers import _get_integer_bits
from .quantizers import get_quantizer
from tensorflow_model_optimization.python.core.sparsity.keras.prunable_layer import PrunableLayer
  def get_prunable_weights(self):
    return []


#
# Constraint class to clip weights and bias between -1 and 1 so that:
#    1. quantization approximation is symmetric (b = 0).
#    2. max(x) and min(x) are 1 and -1 respectively.
#
class Clip(Constraint):
  """Clips weight constraint."""

  # This function was modified from Keras minmaxconstraints.
  #
  # Constrains the weights to be between min/max values.
  #   min_value: the minimum norm for the incoming weights.
  #   max_value: the maximum norm for the incoming weights.
  #   constraint: previous constraint to be clipped.
  #   quantizer: quantizer to be applied to constraint.

  def __init__(self, min_value=0.0, max_value=1.0,
               constraint=None, quantizer=None):
    """Initializes Clip constraint class."""

    self.min_value = min_value
    self.max_value = max_value
    self.constraint = constraints.get(constraint)
    # Don't wrap yourself
    if isinstance(self.constraint, Clip):
      self.constraint = None

# Path: qkeras/quantizers.py
# def _get_integer_bits(min_value,
#                       max_value,
#                       bits=8,
#                       symmetric=False,
#                       keep_negative=False,
#                       is_clipping=True):
#   """Estimates the integer bit(number of bits to the left of the binary point)
#   satisfying the input argument constraints.
# 
#   Args:
#     min_value: A tensor object. Its elements are in float representing the
#       minimum values of ranges.
#     max_value: A tensor object. Its elements are in float representing the
#       maximum values of ranges.
#     bits: number of bits to perform quantization.
#     symmetric: boolean type. if true, it enforces negative and positive ranges
#       to be symmetric.
#     keep_negative: boolean type. if true, we do not clip negative numbers.
#     is_clipping: boolean type. if true, the min_value and max_value are clipped
#       to nearest powers-of-2.
# 
#   Returns:
#     integer_bits : number of bits to the left of the binary point.
#   """
#   # Max the min and max values positive if only using positive values
#   if not keep_negative:
#     min_value = K.maximum(min_value, 0)
#     max_value = K.maximum(max_value, 0)
# 
#   # The number of bits excluding the sign bit
#   unsigned_bits = bits - keep_negative
# 
#   # log2 of absolute min_value and max_value
#   min_value_log2 = K.log(K.abs(min_value)) / np.log(2.0)
#   max_value_log2 = K.log(K.abs(max_value)) / np.log(2.0)
# 
#   # Estimate integer_bits
#   if is_clipping:
#     min_int_bits = tf.math.round(
#         tf.where(min_value_log2 > 0, min_value_log2, 0))
#     max_int_bits = tf.math.round(
#         tf.where(max_value_log2 > 0, max_value_log2, 0))
#   else:
#     min_int_bits = tf.math.ceil(tf.where(min_value_log2 > 0, min_value_log2, 0))
#     max_int_bits = tf.math.ceil(tf.where(max_value_log2 > 0, max_value_log2, 0))
#     # Checks max_value is bounded by the maximum positive value of
#     # pow(2,integer_bits) - pow(2,-fractional_bits).
#     max_value_po2 = pow(2.0, max_int_bits) - pow(
#         2.0, K.minimum(max_int_bits - unsigned_bits, 0))
#     max_int_bits = tf.where(max_value <= max_value_po2, max_int_bits,
#                             max_int_bits + 1)
#     if symmetric:
#       # Checks min_value is bounded by the minimum negative value of
#       # - pow(2,integer_bits) + pow(2,-fractional_bits).
#       min_value_po2 = -pow(2.0, min_int_bits) + pow(
#           2.0, K.minimum(min_int_bits - unsigned_bits, 0))
#       min_int_bits = tf.where(min_value_po2 <= min_value, min_int_bits,
#                               min_int_bits + 1)
# 
#   # To cover both negative and positive ranges with integer_bits.
#   # (For keep_negative=False, min_int_bits is 0.)
#   integer_bits = tf.cast(K.maximum(min_int_bits, max_int_bits), dtype=tf.int32)
#   # It assumes that integer_bits cannot be greater than unsigned_bits
#   integer_bits = K.minimum(unsigned_bits, integer_bits)
# 
#   return integer_bits
# 
# Path: qkeras/quantizers.py
# def get_quantizer(identifier):
#   """Gets the quantizer.
# 
#   Args:
#     identifier: An quantizer, which could be dict, string, or callable function.
# 
#   Returns:
#     A quantizer class or quantization function from this file. For example,
#       Quantizer classes: quantized_bits, quantized_po2, quantized_relu_po2,
#       binary, stochastic_binary, ternary, stochastic_ternary, etc.
# 
#       Quantization functions: binary_sigmoid, hard_sigmoid, soft_sigmoid, etc.
# 
#   Raises:
#     ValueError: An error occurred when quantizer cannot be interpreted.
#   """
# 
#   if identifier is None:
#     return None
#   if isinstance(identifier, dict):
#     return deserialize_keras_object(
#         identifier, module_objects=globals(), printable_module_name="quantizer")
#   elif isinstance(identifier, six.string_types):
#     return safe_eval(identifier, globals())
#   elif callable(identifier):
#     return identifier
#   else:
#     raise ValueError("Could not interpret quantizer identifier: " +
#                      str(identifier))


# Path: qkeras/qlayers.py
import sys
import warnings
import numpy as np
import six
import tensorflow.compat.v2 as tf
import tensorflow.keras.backend as K
from tensorflow.keras import activations
from tensorflow.keras import constraints
from tensorflow.keras import initializers
from tensorflow.keras import regularizers
from tensorflow.keras.constraints import Constraint
from tensorflow.keras.initializers import Initializer
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Layer
from tensorflow.python.framework import smart_cond as tf_utils
from .quantizers import *
from .quantizers import _get_integer_bits
from .quantizers import get_quantizer
from tensorflow_model_optimization.python.core.sparsity.keras.prunable_layer import PrunableLayer

  def get_prunable_weights(self):
    return []


#
# Constraint class to clip weights and bias between -1 and 1 so that:
#    1. quantization approximation is symmetric (b = 0).
#    2. max(x) and min(x) are 1 and -1 respectively.
#
class Clip(Constraint):
  """Clips weight constraint."""

  # This function was modified from Keras minmaxconstraints.
  #
  # Constrains the weights to be between min/max values.
  #   min_value: the minimum norm for the incoming weights.
  #   max_value: the maximum norm for the incoming weights.
  #   constraint: previous constraint to be clipped.
  #   quantizer: quantizer to be applied to constraint.

  def __init__(self, min_value=0.0, max_value=1.0,
               constraint=None, quantizer=None):
    """Initializes Clip constraint class."""

    self.min_value = min_value
    self.max_value = max_value
    self.constraint = constraints.get(constraint)
    # Don't wrap yourself
    if isinstance(self.constraint, Clip):
      self.constraint = None

    self.quantizer = get_quantizer(quantizer)