onnx pooling operator supports asymmetrical padding
        Adding pad operator before pooling in mxnet to work with onnx
186086
def _fix_pooling(self, op_name, inputs, new_attr):
        """"""
        pool_type = 'avg' if op_name == 'AveragePool' else 'max'
        stride = new_attr.get('strides')
        kernel = new_attr.get('kernel_shape')
        padding = new_attr.get('pads')
        pad_width = (0, 0, 0, 0) + _pad_sequence_fix(padding, len(kernel))
        new_pad_op = mx.sym.pad(inputs[0], mode='constant', pad_width=pad_width)
        new_pooling_op = mx.sym.Pooling(new_pad_op, pool_type=pool_type,
                                        stride=stride, kernel=kernel)
        return new_pooling_op
1258519
public static int cudnnPoolingBackward(
        cudnnHandle handle, 
        cudnnPoolingDescriptor poolingDesc, 
        Pointer alpha, 
        cudnnTensorDescriptor yDesc, 
        Pointer y, 
        cudnnTensorDescriptor dyDesc, 
        Pointer dy, 
        cudnnTensorDescriptor xDesc, 
        Pointer x, 
        Pointer beta, 
        cudnnTensorDescriptor dxDesc, 
        Pointer dx)
    {
        return checkResult(cudnnPoolingBackwardNative(handle, poolingDesc, alpha, yDesc, y, dyDesc, dy, xDesc, x, beta, dxDesc, dx));
    }
1118372
def computeMaxPool(input_width):
  
  wout = math.floor((input_width + 2 * PADDING - KERNEL_SIZE) / STRIDE + 1)
  return int(math.floor(wout / 2.0))
541543
def _pad_add_1d(av, size, stlen):
    
    assert len(size) == 1

    padx = _get_pad_left_right(av.shape[0], size[0])

    mask = np.zeros(av.shape, dtype=bool)
    mask[stlen:-stlen] = True
    border = av[~mask]
    if av.dtype.name.count("complex"):
        padval = np.average(np.abs(border)) * \
            np.exp(1j*np.average(np.angle(border)))
    else:
        padval = np.average(border)
    if np.__version__[:3] in ["1.7", "1.8", "1.9"]:
        end_values = ((padval, padval),)
    else:
        end_values = (padval,)
    bv = np.pad(av,
                padx,
                mode="linear_ramp",
                end_values=end_values)
    # roll the array so that the padding values are on the right
    bv = np.roll(bv, -padx[0], 0)
    return bv
704721
def flatten(attrs, inputs, proto_obj):
    """"""
    #Mxnet does not have axis support. By default uses axis=1
    if 'axis' in attrs and attrs['axis'] != 1:
        raise RuntimeError("Flatten operator only supports axis=1")
    new_attrs = translation_utils._remove_attributes(attrs, ['axis'])
    return 'Flatten', new_attrs, inputs
71426
public static int cudnnPoolingForward(
        cudnnHandle handle, 
        cudnnPoolingDescriptor poolingDesc, 
        Pointer alpha, 
        cudnnTensorDescriptor xDesc, 
        Pointer x, 
        Pointer beta, 
        cudnnTensorDescriptor yDesc, 
        Pointer y)
    {
        return checkResult(cudnnPoolingForwardNative(handle, poolingDesc, alpha, xDesc, x, beta, yDesc, y));
    }
1271965
def pad(n,replace: true)
      if padding? && replace
          strip_padding.pad(n)
      else
        pad = [n] * n
        return CryptBuffer(bytes + pad )
      end
    end
235904
def deconv(attrs, inputs, proto_obj):
    """"""
    new_attrs = translation_utils._fix_attribute_names(attrs, {'kernel_shape' : 'kernel',
                                                               'strides' : 'stride',
                                                               'pads': 'pad',
                                                               'dilations': 'dilate',
                                                               'group': 'num_group'})
    new_attrs = translation_utils._add_extra_attributes(new_attrs, {'num_group' : 1})
    new_attrs = translation_utils._fix_bias('Deconvolution', new_attrs, len(inputs))

    new_attrs = translation_utils._fix_channels('Deconvolution', new_attrs, inputs, proto_obj)
    kernel = new_attrs['kernel']
    stride = new_attrs['stride'] if 'stride' in new_attrs else []
    padding = new_attrs['pad'] if 'pad' in new_attrs else []
    dilations = new_attrs['dilate'] if 'dilate' in new_attrs else []
    num_filter = new_attrs['num_filter']
    num_group = new_attrs['num_group']
    no_bias = new_attrs['no_bias'] if 'no_bias' in new_attrs else False
    bias = None if no_bias is True else inputs[2]

    # Unlike ONNX, MXNet's deconvolution operator does not support asymmetric padding, so we first
    # use 'Pad' operator, which supports asymmetric padding. Then use the deconvolution operator.
    pad_width = (0, 0, 0, 0) + translation_utils._pad_sequence_fix(padding, kernel_dim=len(kernel))
    pad_op = symbol.pad(inputs[0], mode='constant', pad_width=pad_width)

    deconv_op = symbol.Deconvolution(pad_op, inputs[1], bias,
                                     kernel=kernel, stride=stride, dilate=dilations,
                                     num_filter=num_filter, num_group=num_group, no_bias=no_bias)

    return deconv_op, new_attrs, inputs
113897
private byte[] bufX( long bias, int scale, int off, int log ) {
    byte[] bs = new byte[(_len<<log)+off];
    int j = 0;
    for( int i=0; i<_len; i++ ) {
      long le = -bias;
      if(_id == null || _id.length == 0 || (j < _id.length && _id[j] == i)){
        if( isNA2(j) ) {
          le = NAS[log];
        } else {
          int x = (_xs[j]==Integer.MIN_VALUE+1 ? 0 : _xs[j])-scale;
          le += x >= 0
              ? _ls[j]*PrettyPrint.pow10i( x)
              : _ls[j]/PrettyPrint.pow10i(-x);
        }
        ++j;
      }
      switch( log ) {
        case 0:          bs [i    +off] = (byte)le ; break;
        case 1: UDP.set2(bs,(i<<1)+off,  (short)le); break;
        case 2: UDP.set4(bs,(i<<2)+off,    (int)le); break;
        case 3: UDP.set8(bs,(i<<3)+off,         le); break;
        default: throw H2O.fail();
      }
    }
    assert j == _sparseLen:"j = " + j + ", len = " + _sparseLen + ", len2 = " + _len + ", id[j] = " + _id[j];
    return bs;
  }
1103562
def pad_for_tpu(shapes_dict, hparams, max_length):
  """"""
  padded_shapes = {}

  def get_filler(specified_max_length):
    if not specified_max_length:
      return max_length
    return min(specified_max_length, max_length)

  inputs_none_filler = get_filler(hparams.max_input_seq_length)
  targets_none_filler = get_filler(hparams.max_target_seq_length)

  def pad_one_shape(shape, none_filler):
    return [
        (dim if dim is not None else none_filler) for dim in shape.as_list()
    ]

  for key, shape in six.iteritems(shapes_dict):
    if key == "inputs":
      padded_shapes[key] = pad_one_shape(shape, inputs_none_filler)
    elif key == "targets":
      padded_shapes[key] = pad_one_shape(shape, targets_none_filler)
    else:
      padded_shapes[key] = pad_one_shape(shape, max_length)
  return padded_shapes
429780
def encoder(nef, z_dim, batch_size, no_bias=True, fix_gamma=True, eps=1e-5 + 1e-12):
    '''
    '''
    BatchNorm = mx.sym.BatchNorm

    data = mx.sym.Variable('data')

    e1 = mx.sym.Convolution(data, name='enc1', kernel=(5,5), stride=(2,2), pad=(2,2), num_filter=nef, no_bias=no_bias)
    ebn1 = BatchNorm(e1, name='encbn1', fix_gamma=fix_gamma, eps=eps)
    eact1 = mx.sym.LeakyReLU(ebn1, name='encact1', act_type='leaky', slope=0.2)

    e2 = mx.sym.Convolution(eact1, name='enc2', kernel=(5,5), stride=(2,2), pad=(2,2), num_filter=nef*2, no_bias=no_bias)
    ebn2 = BatchNorm(e2, name='encbn2', fix_gamma=fix_gamma, eps=eps)
    eact2 = mx.sym.LeakyReLU(ebn2, name='encact2', act_type='leaky', slope=0.2)

    e3 = mx.sym.Convolution(eact2, name='enc3', kernel=(5,5), stride=(2,2), pad=(2,2), num_filter=nef*4, no_bias=no_bias)
    ebn3 = BatchNorm(e3, name='encbn3', fix_gamma=fix_gamma, eps=eps)
    eact3 = mx.sym.LeakyReLU(ebn3, name='encact3', act_type='leaky', slope=0.2)

    e4 = mx.sym.Convolution(eact3, name='enc4', kernel=(5,5), stride=(2,2), pad=(2,2), num_filter=nef*8, no_bias=no_bias)
    ebn4 = BatchNorm(e4, name='encbn4', fix_gamma=fix_gamma, eps=eps)
    eact4 = mx.sym.LeakyReLU(ebn4, name='encact4', act_type='leaky', slope=0.2)

    eact4 = mx.sym.Flatten(eact4)

    z_mu = mx.sym.FullyConnected(eact4, num_hidden=z_dim, name="enc_mu")
    z_lv = mx.sym.FullyConnected(eact4, num_hidden=z_dim, name="enc_lv")

    z = z_mu + mx.symbol.broadcast_mul(mx.symbol.exp(0.5*z_lv),mx.symbol.random_normal(loc=0, scale=1,shape=(batch_size,z_dim)))

    return z_mu, z_lv, z
815956
def convert_pooling(builder, layer, input_names, output_names, keras_layer):
    
    _check_data_format(keras_layer)
    # Get input and output names
    input_name, output_name = (input_names[0], output_names[0])

    # Pooling layer type
    if isinstance(keras_layer, _keras.layers.convolutional.MaxPooling2D) or \
        isinstance(keras_layer, _keras.layers.convolutional.MaxPooling1D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalMaxPooling2D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalMaxPooling1D):
        layer_type_str = 'MAX'
    elif isinstance(keras_layer, _keras.layers.convolutional.AveragePooling2D) or \
        isinstance(keras_layer, _keras.layers.convolutional.AveragePooling1D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalAveragePooling2D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalAveragePooling1D):
        layer_type_str = 'AVERAGE'
    else:
        raise TypeError("Pooling type %s not supported" % keras_layer)

    # if it's global, set the global flag
    if isinstance(keras_layer, _keras.layers.pooling.GlobalMaxPooling2D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalAveragePooling2D):
        # 2D global pooling
        global_pooling = True
        height, width = (0, 0)
        stride_height, stride_width = (0,0)
        padding_type = 'VALID'
    elif isinstance(keras_layer, _keras.layers.pooling.GlobalMaxPooling1D) or \
        isinstance(keras_layer, _keras.layers.pooling.GlobalAveragePooling1D):
        # 1D global pooling: 1D global pooling seems problematic in the backend,
        # use this work-around
        global_pooling = False
        _, width, channels = keras_layer.input_shape
        height = 1
        stride_height, stride_width = height, width
        padding_type = 'VALID'
    else:
        global_pooling = False
        # Set pool sizes and strides
        # 1D cases:
        if isinstance(keras_layer, _keras.layers.convolutional.MaxPooling1D) or \
            isinstance(keras_layer, _keras.layers.pooling.GlobalMaxPooling1D) or \
            isinstance(keras_layer, _keras.layers.convolutional.AveragePooling1D) or \
            isinstance(keras_layer, _keras.layers.pooling.GlobalAveragePooling1D):
            pool_size = keras_layer.pool_size if type(keras_layer.pool_size) is int else keras_layer.pool_size[0]
            height, width = 1, pool_size
            if keras_layer.strides is not None:
                strides = keras_layer.strides if type(keras_layer.strides) is int else keras_layer.strides[0]
                stride_height, stride_width = 1, strides
            else:
                stride_height, stride_width = 1, pool_size
        # 2D cases:
        else:
            height, width = keras_layer.pool_size
            if keras_layer.strides is None:
                stride_height, stride_width = height, width
            else:
                stride_height, stride_width = keras_layer.strides

        # Padding
        padding = keras_layer.padding
        if keras_layer.padding == 'valid':
            padding_type = 'VALID'
        elif keras_layer.padding == 'same':
            padding_type = 'SAME'
        else:
            raise TypeError("Border mode %s not supported" % padding)

    builder.add_pooling(name = layer,
        height = height,
        width = width,
        stride_height = stride_height,
        stride_width = stride_width,
        layer_type = layer_type_str,
        padding_type = padding_type,
        input_name = input_name,
        output_name = output_name,
        exclude_pad_area = True,
        is_global = global_pooling)
103132
def calculate_pooling_output_shapes(operator):
    '''
    
    '''
    check_input_and_output_numbers(operator, input_count_range=1, output_count_range=1)
    check_input_and_output_types(operator, good_input_types=[FloatTensorType])

    input = operator.inputs[0]
    input_shape = operator.inputs[0].type.shape

    if len(input.type.shape) != 4:
        raise RuntimeError('Input must be 4-D float tensor')

    operator.outputs[0].type.shape = [0, 0, 0, 0]
    output_shape = operator.outputs[0].type.shape

    # Adjust N-axis
    output_shape[0] = input_shape[0]

    # Adjust C-axis
    output_shape[1] = input_shape[1]

    params = operator.raw_operator.pooling
    # Set up default and non-default parameters. Notice that they are only set for H- and W-axes.
    dilations = [1, 1]  # CoreML Pooling doesn't allow dilation, so we use [1, 1] which is equivalent to no dilation.
    kernel_shape = [3, 3]
    if len(params.kernelSize) > 0:
        kernel_shape = params.kernelSize
    strides = [1, 1]
    if len(params.stride) > 0:
        strides = params.stride
    pad_mode = params.WhichOneof('PoolingPaddingType')
    if pad_mode == 'valid' and len(params.valid.paddingAmounts.borderAmounts) > 0:
        pad_amounts = params.valid.paddingAmounts.borderAmounts
        pad_heads = [pad_amounts[0].startEdgeSize, pad_amounts[1].startEdgeSize]
        pad_tails = [pad_amounts[0].endEdgeSize, pad_amounts[1].endEdgeSize]
    elif pad_mode == 'includeLastPixel' and len(params.includeLastPixel.paddingAmounts) > 0:
        pad_amounts = params.includeLastPixel.paddingAmounts
        pad_heads = [pad_amounts[0], pad_amounts[1]]
        pad_tails = [pad_amounts[0], pad_amounts[1]]
    else:
        # For same padding, padding amounts are not used
        pad_heads = [0, 0]
        pad_tails = [0, 0]

    # Calculate output shape along H- and W-axes
    for i in range(2):
        output_shape[i + 2] = calculate_convolution_and_pooling_1D_output_shape(
            input_shape[i + 2], kernel_shape[i], dilations[i], strides[i],
            pad_mode, pad_heads[i], pad_tails[i], params.globalPooling)
120518
def MaxPool(a, k, strides, padding, data_format):
    
    if data_format.decode("ascii") == "NCHW":
        a = np.rollaxis(a, 1, -1),

    patches = _pool_patches(a, k, strides, padding.decode("ascii"))
    pool = np.amax(patches, axis=tuple(range(-len(k), 0)))

    if data_format.decode("ascii") == "NCHW":
        pool = np.rollaxis(pool, -1, 1)

    return pool,
751800
def get_lenet():
    
    data = mx.symbol.Variable('data')

    # first conv
    conv1 = mx.symbol.CaffeOp(data_0=data, num_weight=2,
                              prototxt="layer{type:\"Convolution\" "
                                       "convolution_param { num_output: 20 kernel_size: 5 stride: 1} }")
    act1 = mx.symbol.CaffeOp(data_0=conv1, prototxt="layer{type:\"TanH\"}")
    pool1 = mx.symbol.CaffeOp(data_0=act1,
                              prototxt="layer{type:\"Pooling\" pooling_param { pool: MAX kernel_size: 2 stride: 2}}")

    # second conv
    conv2 = mx.symbol.CaffeOp(data_0=pool1, num_weight=2,
                              prototxt="layer{type:\"Convolution\" "
                                       "convolution_param { num_output: 50 kernel_size: 5 stride: 1} }")
    act2 = mx.symbol.CaffeOp(data_0=conv2, prototxt="layer{type:\"TanH\"}")
    pool2 = mx.symbol.CaffeOp(data_0=act2,
                              prototxt="layer{type:\"Pooling\" pooling_param { pool: MAX kernel_size: 2 stride: 2}}")

    fc1 = mx.symbol.CaffeOp(data_0=pool2, num_weight=2,
                            prototxt="layer{type:\"InnerProduct\" inner_product_param{num_output: 500} }")
    act3 = mx.symbol.CaffeOp(data_0=fc1, prototxt="layer{type:\"TanH\"}")

    # second fullc
    fc2 = mx.symbol.CaffeOp(data_0=act3, num_weight=2,
                            prototxt="layer{type:\"InnerProduct\"inner_product_param{num_output: 10} }")
    if use_caffe_loss:
        label = mx.symbol.Variable('softmax_label')
        lenet = mx.symbol.CaffeLoss(data=fc2, label=label, grad_scale=1, name='softmax',
                                    prototxt="layer{type:\"SoftmaxWithLoss\"}")
    else:
        lenet = mx.symbol.SoftmaxOutput(data=fc2, name='softmax')
    return lenet
784937
def adaptive_avgmax_pool2d(x, pool_type='avg', padding=0, count_include_pad=False):
    
    if pool_type == 'avgmaxc':
        x = torch.cat([
            F.avg_pool2d(
                x, kernel_size=(x.size(2), x.size(3)), padding=padding, count_include_pad=count_include_pad),
            F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding)
        ], dim=1)
    elif pool_type == 'avgmax':
        x_avg = F.avg_pool2d(
                x, kernel_size=(x.size(2), x.size(3)), padding=padding, count_include_pad=count_include_pad)
        x_max = F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding)
        x = 0.5 * (x_avg + x_max)
    elif pool_type == 'max':
        x = F.max_pool2d(x, kernel_size=(x.size(2), x.size(3)), padding=padding)
    else:
        if pool_type != 'avg':
            print('Invalid pool type %s specified. Defaulting to average pooling.' % pool_type)
        x = F.avg_pool2d(
            x, kernel_size=(x.size(2), x.size(3)), padding=padding, count_include_pad=count_include_pad)
    return x
184151
def set_nodes(self, func):
        """"""
        op_type = self.nnabla_function_type_to_onnx_optype.get(func.type)
        if op_type is None:
            raise ValueError(
                "function {} is currently not supported for ONNX conversion".format(func.type))
        if callable(op_type):
            return op_type(func)

        variables = self._net.variable
        input_types = self._input_types
        output_types = self._output_types
        broadcast_target = self._broadcast_target

        n = onnx.helper.make_node(
            op_type,
            func.input,
            func.output,
            name=func.name)
        nl = []
        if func.type == "Convolution":
            cp = func.convolution_param
            # Calculate the kernel_shape from input weight data.
            # Weight data should be the second input for convolution
            if len(func.input) < 2:
                raise ValueError(
                    "Weight input is missing for convolution {}"
                    .format(func.name))
            weight = func.input[1]
            weight_var = [v for v in variables if v.name == weight]
            if len(weight_var) != 1:
                raise ValueError(
                    "No weight input was found, or multiple weight inputs were found"
                    " for convolution {} where there should be only one."
                    .format(func.name))
            weight_shape = weight_var[0].shape
            # The base axis for weights is the next axis from the data's base axis
            weight_base = cp.base_axis + 1
            k = onnx.helper.make_attribute("kernel_shape",
                                           weight_shape.dim[weight_base:])
            d = onnx.helper.make_attribute("dilations", cp.dilation.dim)
            s = onnx.helper.make_attribute("strides", cp.stride.dim)
            p = onnx.helper.make_attribute("pads", cp.pad.dim[:] * 2)
            g = onnx.helper.make_attribute("group", cp.group)
            n.attribute.extend([k, d, s, p, g])
            nl.append(n)
        elif func.type == "GlobalAveragePooling":
            # We wipeout the node name to avoid a bug?
            # that occurs when we use a GlobalAveragePooling node with a name
            # "Conv" or "Pool" contained.
            # Caffe2 issue is here:
            # https://github.com/caffe2/caffe2/issues/1971
            # Because a GlobalAveragePooling operator does not contain a kernel, we get an error at the
            # following code if we have a specific name.
            # https://github.com/caffe2/caffe2/blob/master/caffe2/operators/conv_pool_op_base.h#L167
            # The above caffe2 code should be checking the node's operator name and not the node's name.
            n.name = ""
            nl.append(n)
        elif func.type == "Softmax":
            # Softmax on NNabla does softmax ONLY along the specified axis.
            # ONNX first squashes the input dimensions to 2D based on the specified axis,
            # and then calculates the Softmax.
            # Since these two slightly differ, we show a warning here.
            logger.warning(SOFTMAX_WARNING)
            attr = onnx.helper.make_attribute("axis", func.softmax_param.axis)
            n.attribute.extend([attr])
            nl.append(n)
        elif func.type == "Reshape":
            # Convert Reshape size to a constant
            rp = func.reshape_param
            x = func.input[0]
            c_out = x + "_shape"
            c = generate_constant(c_out, func.name + "_shape",
                                  TensorProto.INT32, [len(rp.shape.dim)],
                                  rp.shape.dim)
            nl.append(c)
            # Add resize target shape as the second input
            del n.input[:]
            n.input.extend([x, c_out])
            nl.append(n)
        elif func.type == "Transpose":
            tp = func.transpose_param
            p = onnx.helper.make_attribute("perm", tp.axes)
            n.attribute.extend([p])
            nl.append(n)
        elif func.type == "BatchMatmul":
            bmp = func.batch_matmul_param
            if bmp.transpose_a or bmp.transpose_b:
                raise ValueError(
                    "{} with transpose is not supported yet".format(func.type))
            nl.append(n)
        elif func.type == "LeakyReLU":
            lrp = func.leaky_relu_param
            a = onnx.helper.make_attribute("alpha", lrp.alpha)
            n.attribute.extend([a])
            nl.append(n)
        elif func.type == "ELU":
            ep = func.elu_param
            a = onnx.helper.make_attribute("alpha", ep.alpha)
            n.attribute.extend([a])
            nl.append(n)
        elif func.type == "LogicalNot":
            # Store the input/output tensor's name and convert it to boolean
            input_types[n.input[0]] = TensorProto.BOOL
            output_types[n.output[0]] = TensorProto.BOOL
            nl.append(n)
        elif func.type == "SELU":
            sp = func.selu_param
            a = onnx.helper.make_attribute("alpha", sp.alpha)
            g = onnx.helper.make_attribute("gamma", sp.scale)
            n.attribute.extend([a, g])
            nl.append(n)
        elif func.type == "Sum":
            sp = func.sum_param
            set_reduction_attrs(n, sp)
            nl.append(n)
        elif func.type == "Mean":
            mp = func.mean_param
            set_reduction_attrs(n, mp)
            nl.append(n)
        elif func.type == "Max":
            mp = func.max_param
            set_reduction_attrs(n, mp)
            nl.append(n)
        elif func.type == "Min":
            mp = func.min_param
            set_reduction_attrs(n, mp)
            nl.append(n)
        elif func.type == "Prod":
            pp = func.prod_param
            set_reduction_attrs(n, pp)
            nl.append(n)
        elif func.type == "BroadcastTo":
            # BroadcastTo conversion only works when the
            # broadcasted buffer is used as second input for the following:
            # Add, And, Div, Equal, Greater,
            # Less, Mul, Or, Pow, Sub, Xor
            bp = func.broadcast_to_param
            broadcast_target[func.output[0]] = (func.input[1], bp.axis)
            # we do not append node here because BroadcastTo should disappear
        elif func.type == "MinimumScalar":
            msp = func.minimum_scalar_param
            m = onnx.helper.make_attribute("max", msp.val)
            n.attribute.extend([m])
            nl.append(n)
        elif func.type == "MaximumScalar":
            msp = func.maximum_scalar_param
            m = onnx.helper.make_attribute("min", msp.val)
            n.attribute.extend([m])
            nl.append(n)
        elif func.type == "Pad":
            pp = func.pad_param
            mode_conv = {
                "constant": "constant",
                "replicate": "edge",
                "reflect": "reflect"
            }
            # separate pad values to match ONNX format
            # (S0,E0,S1,E1) => (S0,S1,E0,E1)
            dim = len(pp.pad_width) // 2
            # If we can get the dimension of the input buffer,
            # we get it here. If we cannot, we are assuming 4D input
            in_name = func.input[0]
            in_var = [v for v in variables if v.name == in_name]
            in_dim = 4
            if len(in_var) == 1 and len(in_var[0].shape.dim) > 0:
                # Found variable with valid shape.
                # If the shape dimension is zero, it means
                # that is an intermediate buffer so we can't get
                # the exact dimension at this point
                # (thus assuming 4D input).
                in_dim = len(in_var[0].shape.dim)
            elif len(in_var) > 1:
                raise ValueError("More than one buffer with"
                                 " the same buffer name found.")
            zero_dim_num = in_dim - dim
            it = iter(pp.pad_width)
            # We need to fill empty dimensions with zero padding
            # (at least this is what Caffe2 expects)
            starts = [0] * zero_dim_num
            ends = [0] * zero_dim_num
            for x in it:
                starts.append(x)
                ends.append(next(it))
            starts.extend(ends)
            pad = onnx.helper.make_attribute("pads", starts)
            m = onnx.helper.make_attribute("mode", mode_conv[pp.mode])
            v = onnx.helper.make_attribute("value", pp.constant_value)
            n.attribute.extend([pad, m, v])
            nl.append(n)
        else:
            # Simply append node to list
            nl.append(n)
        return nl