code: def min_max_quantize(
      x,
      ql_min=0,
      ql_max=255,
      decay=0.999,
      x_min_max=False,
      ema=False,
      ste_fine_grained=True,
      eps=0.01,
      qr_min_init=None,
      qr_max_init=None,
      fix_parameters=False,
      outputs=None,
  ):
      r"""Min-max quantization.
  
      This function uniformly quantizes values in the range of min and max quantization levels.
  
      Min-max quantization is defined as the following equation
  
      .. math::
  
          y = round \left(\frac{\min(\max(x, m), M) - m}{scale} \right) \times scale + m, 
  
      where the :math:`scale` is defined as 
  
      .. math::
  
          scale = \frac{M - m}{M_q - m_q}, 
  
      and 
  
      .. math::
  
          m_q = ql_{min}, \\
          M_q = ql_{max}, \\
          m = qr_{min}, \\
          M = qr_{max}.
  
      In the backward pass when using `ste_fine_grained` as false,
  
          .. math::
  
            \frac{\partial q_i}{\partial x_i} = 1.
  
  
      In the backward pass when using `ste_fine_grained` as true,
  
          .. math::
  
             \frac{\partial q_i}{\partial x_i}= \left\{
           \begin{array}{ll}
             0 & if \ \ \ x_i > M \\
             1 & if \ \ m \le x_i \le M \\
             0 & if \ \ x_i < m \\
           \end{array} \right..
  
      :math:`qr_{min}` and :math:`qr_{max}` are treaded as follows.
  
          * `x_min_max` is `True` and `ema` is `True`: 
            Exponential moving average are computed for each :math:`min(x)` and :math:`max(x)` 
            then stored in :math:`qr_{min}` and :math:`qr_{max}`.
          * `x_min_max` is `True` and `ema` is `False`:
            :math:`min(x)` and :math:`max(x)` are computed then stored in :math:`qr_{min}` and :math:`qr_{max}`.
          * `x_min_max` is `False` and `ema` is `True`:
            Exponential moving average stored in :math:`qr_{min}` and :math:`qr_{max}` are used.
          * `x_min_max` is `False` and `ema` is `False`
            Gradients of :math:`qr_{min}` and :math:`qr_{max}` are computed in the backward pass.
  
      More precisely, in inference of the min-max quantization, one has to consider *zero-point (zp)*
      which corresponds
      to the real value 0, and its data type is an integer. *zero-point* is defined as 
  
          .. math::
  
             && zp_f = ql_{min} -\frac{qr_{min}}{scale}, \\
             && zp = \left\{
           \begin{array}{ll}
             ql_{max} & if \ \ \ zp_f >= ql_{max} \\
             round(zp_f) & if \ \ otherwise \\
             ql_{min}  & if \ \ zp_f <= ql_{min} \\
           \end{array} \right..
  
      Accordingly, in order to simulate quantization effect of *zero-point*, 
      during both forward and backward pass, :math:`qr_{min}` and :math:`qr_{max}` are adjusted as follows,
  
          .. math::
  
             qr_{min}^{adj} = ql_{min} - zp * scale, \\
             qr_{max}^{adj} = ql_{max} - zp * scale.
  
      These operations are often called *nudge*. 
  
      Finally, in the formulas of the min-max quantization, :math:`m` and :math:`M` are replaced by
      :math:`qr_{min}^{adj}` and :math:`qr_{max}^{adj}` respectively.
  
      Args:
          x (~nnabla.Variable): Input N-D array.
          ql_min (int, float, or ~nnabla.Variable): Minimum quantization level. Default is 0.
          ql_max (int, float, or ~nnabla.Variable): Maximum quantization level. Default is 255.
          decay (float): The decay rate for the exponential moving average.
          x_min_max (bool): Use the min and max of x to compute quantization ranges. Default is `False`.
          ema (bool): Use the exponential moving average for the min and max quantization ranges.
                      Default is `False`.
          ste_fine_grained (bool): If true, STE is not 1, the {0, 1}-mask computed from the min-max is applied to the gradient in the backward; otherwise, STE is 1.
          eps (float): Epsilon, or small value to ensure :math:`qr_{max} - qr_{min}` must be greater
                       than the epsilon for both weights and bias.
          qr_min_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for the minimum quantization range, qr_min. Default is :obj:`nnabla.initializer.ConstantInitializer` (-6.0).
          qr_max_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for the maximum quantization range, qr_max Default is :obj:`nnabla.initializer.ConstantInitializer` (6.0).
          fix_parameters (bool): When set to `True`, the weights and biases will not be updated.
  
      References:
          Benoit Jacob, Skirmantas Kligys, Bo Chen, Menglong Zhu, Matthew Tang, Andrew Howard, Hartwig Adam, and Dmitry Kalenichenko, "Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference", https://arxiv.org/abs/1712.05877
  
      """
      # ql_min and ql_max
      if isinstance(ql_min, (int, float)):
          reshape = [1 for _ in range(x.ndim)]
          ql_min = np.array(ql_min).reshape(reshape)
          ql_min = get_parameter_or_create(
              "ql_min", reshape, ql_min, False, False
          )
      if isinstance(ql_max, (int, float)):
          reshape = [1 for _ in range(x.ndim)]
          ql_max = np.array(ql_max).reshape(reshape)
          ql_max = get_parameter_or_create(
              "ql_max", reshape, ql_max, False, False
          )
      # qr_min and qr_max
      qr_min_init = (
          qr_min_init if qr_min_init else ConstantInitializer(-6.0)
      )
      qr_max_init = (
          qr_max_init if qr_max_init else ConstantInitializer(6.0)
      )
      shape = ql_min.shape
      qr_min = get_parameter_or_create(
          "qr_min",
          shape,
          qr_min_init,
          not (x_min_max and ema),
          not fix_parameters,
      )
      qr_max = get_parameter_or_create(
          "qr_max",
          shape,
          qr_max_init,
          not (x_min_max and ema),
          not fix_parameters,
      )
      x_q = F.min_max_quantize(
          x,
          qr_min,
          qr_max,
          ql_min,
          ql_max,
          decay,
          x_min_max,
          ema,
          ste_fine_grained,
          eps,
          outputs=outputs,
      )
      return x_q
  
imports: from six import exec_
  import numpy
  import nnabla
  import nnabla.functions
  from nnabla.parameter import get_parameter_or_create, get_parameter
  from nnabla.initializer import calc_uniform_lim_glorot, ConstantInitializer, NormalInitializer, UniformInitializer, WeightNormalizationScaleInitializer
label_desc: Use image node_id 35 for calling a global function with example usage: min_max_quantize(x, ql_min, ql_max, decay, x_min_max, ema, ste_fine_grained, eps, qr_min_init, qr_max_init, fix_parameters, outputs) and returns: x_q