code: def min_max_quantized_convolution(
      inp,
      outmaps,
      kernel,
      pad=None,
      stride=None,
      dilation=None,
      group=1,
      channel_last=False,
      w_init=None,
      b_init=None,
      base_axis=1,
      fix_parameters=False,
      rng=None,
      with_bias=True,
      quantize_w=True,
      ql_min_w=0,
      ql_max_w=255,
      w_min_max=False,
      qr_min_w_init=None,
      qr_max_w_init=None,
      ste_fine_grained_w=True,
      quantize_b=True,
      ql_min_b=0,
      ql_max_b=255,
      b_min_max=False,
      qr_min_b_init=None,
      qr_max_b_init=None,
      ste_fine_grained_b=True,
      eps=0.01,
  ):
      r"""Min-max Quantized Convolution.
  
      Min-max Quantized Convolution is the convolution function,
      except the definition of the inner product is modified.
      The input-output relation of this function is as follows:
  
      .. math::
  
          y_{n, a, b} = \sum_{m} \sum_{i} \sum_{j} Q(w_{n, m, i, j}) x_{m, a + i, b + j},
  
      where :math:`Q(w_{n, m, i, j})` is the min-max quantization function.
  
      In the min_max_quantized convolution, the exponential moving average is not used.
      the min and max quantization ranges are either the min-max of weights and bias or trained.
  
      Notice that the min and max values of inputs are always used instead of the exponential moving average.
  
      .. note::
  
          1) if you would like to share weights between some layers, please
          make sure to share the standard, floating value weights (`weight`)
          and not the quantized weights (`quantized weight`)
  
          2) The weights and the quantized weights become synced only after :func:`~nnabla._variable.Variable.forward` is called,
          and not after a call to :func:`~nnabla._variable.Variable.backward`.
          To access the parameters of the network, remember to call :func:`~nnabla._variable.Variable.forward` once before doing so, otherwise the
          float weights and the quantized weights will not be in sync.
  
          3) CPU and GPU implementations now use float value for `quantized weight`,
          since this function is only for simulation purposes.
  
      Args:
          inp (~nnabla.Variable): N-D array.
          outmaps (int): Number of convolution kernels (which is equal to the number of output channels). For example, to apply convolution on an input with 16 types of filters, specify 16.
          kernel (:obj:`tuple` of :obj:`int`): Convolution kernel size. For example, to apply convolution on an image with a 3 (height) by 5 (width) two-dimensional kernel, specify (3,5).
          pad (:obj:`tuple` of :obj:`int`): Padding sizes for dimensions.
          stride (:obj:`tuple` of :obj:`int`): Stride sizes for dimensions.
          dilation (:obj:`tuple` of :obj:`int`): Dilation sizes for dimensions.
          group (int): Number of groups of channels. This makes connections across channels more sparse by grouping connections along map direction.
          channel_last (bool): If True, the last dimension is considered as channel dimension, a.k.a. NHWC order.
          w_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for weight. By default, it is initialized with :obj:`nnabla.initializer.UniformInitializer` within the range determined by :obj:`nnabla.initializer.calc_uniform_lim_glorot`.
          b_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for bias. By default, it is initialized with zeros if `with_bias` is `True`.
          base_axis (int): Dimensions up to `base_axis` are treated as the sample dimensions.
          fix_parameters (bool): When set to `True`, the weights and biases will not be updated.
          rng (numpy.random.RandomState): Random generator for Initializer.
          with_bias (bool): Specify whether to include the bias term.
          quantize_w (bool): Quantize weights if `True`.
          ql_min_w (int, float, or ~nnabla.Variable): Minimum quantization level for weights. Default is 0.
          ql_max_w (int, float, or ~nnabla.Variable): Maximum quantization level for weights. Default is 255.
          w_min_max (bool): Use the min and max of weights to compute quantization ranges. Default is `False`.
          qr_min_w_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for the minimum quantization range, qr_min. Default is :obj:`nnabla.initializer.ConstantInitializer` (-2.0).
          qr_max_w_init (:obj:`nnabla.initializer.BaseInitializer` or :obj:`numpy.ndarray`): Initializer for the maximum quantization range, qr_max Default is :obj:`nnabla.initializer.ConstantInitializer` (2.0).
          ste_fine_grained_w (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.
          quantize_b (bool): Quantize bias if `True`.
          ql_min_b (int, float, or ~nnabla.Variable): Minimum quantization level for bias. Default is 0.
          ql_max_b (int, float, or ~nnabla.Variable): Maximum quantization level for bias. Default is 255.
          b_min_max (bool): Use the min and max of bias to compute quantization ranges. Default is `False`.
          qr_min_b_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_b_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).
          ste_fine_grained_b (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.
      Returns:
          :class:`~nnabla.Variable`: N-D array.
  
      """
      if channel_last:
          channels = inp.shape[-1]
          filter_shape = tuple(kernel) + (channels // group,)
      else:
          channels = inp.shape[base_axis]
          filter_shape = (channels // group,) + tuple(kernel)
      if w_init is None:
          w_init = UniformInitializer(
              calc_uniform_lim_glorot(channels, outmaps, tuple(kernel)),
              rng=rng,
          )
      if with_bias and b_init is None:
          b_init = ConstantInitializer()
      if qr_min_w_init is None:
          qr_min_w_init = ConstantInitializer(-2.0)
      if qr_max_w_init is None:
          qr_max_w_init = ConstantInitializer(2.0)
      if qr_min_b_init is None:
          qr_min_b_init = ConstantInitializer(-6.0)
      if qr_max_b_init is None:
          qr_max_b_init = ConstantInitializer(6.0)
  
      # Floating Weight
      w = get_parameter_or_create(
          "W",
          (outmaps,) + filter_shape,
          w_init,
          True,
          not fix_parameters,
      )
  
      # Quantized Weight
      if quantize_w:
          w_q = get_parameter_or_create(
              "W_q", (outmaps,) + filter_shape, w_init, False
          )
          # Link computation graph
          real_w_q = min_max_quantize(
              w,
              ql_min_w,
              ql_max_w,
              0.999,
              w_min_max,
              False,
              qr_min_init=qr_min_w_init,
              qr_max_init=qr_max_w_init,
              ste_fine_grained=ste_fine_grained_w,
              eps=eps,
              fix_parameters=fix_parameters,
              outputs=[w_q.data],
              name="min_max_quantize_w",
          )
          real_w_q.persistent = True
      else:
          real_w_q = w
  
      # Bias
      # Floating
      b = None
      b_q = None
      real_b_q = None
  
      if with_bias:
          b = get_parameter_or_create(
              "b", (outmaps,), b_init, True, not fix_parameters
          )
          if quantize_b:
              b_q = get_parameter_or_create(
                  "b_q", (outmaps,), b_init, False
              )
              # Link computation graph
              real_b_q = min_max_quantize(
                  b,
                  ql_min_b,
                  ql_max_b,
                  0.999,
                  b_min_max,
                  False,
                  qr_min_init=qr_min_b_init,
                  qr_max_init=qr_max_b_init,
                  ste_fine_grained=ste_fine_grained_b,
                  eps=eps,
                  fix_parameters=fix_parameters,
                  outputs=[b_q.data],
                  name="min_max_quantize_b",
              )
              real_b_q.persistent = True
          else:
              real_b_q = b
  
      return F.convolution(
          inp,
          real_w_q,
          real_b_q,
          base_axis,
          pad,
          stride,
          dilation,
          group,
          channel_last,
      )
  
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 37 for calling a global function with example usage: min_max_quantized_convolution(inp, outmaps, kernel, pad, stride, dilation, group, channel_last, w_init, b_init, base_axis, fix_parameters, rng, with_bias, quantize_w, ql_min_w, ql_max_w, w_min_max, qr_min_w_init, qr_max_w_init, ste_fine_grained_w, quantize_b, ql_min_b, ql_max_b, b_min_max, qr_min_b_init, qr_max_b_init, ste_fine_grained_b, eps) and returns: F