from __future__ import absolute_import from .Node import Op from .._base import DNNL_LIB from ..cpu_links import pad as cpu_pad from ..cpu_links import pad_gradient as cpu_pad_gradient from ..gpu_links import pad from ..gpu_links import pad_gradient class PadOp(Op): def __init__(self, node_A, paddings, mode="CONSTANT", constant_values=0, ctx=None): """Creates a node that represents np.sum(node_A, axis=0). Only support common-case axis=0 reduction for simplicity of gradient. """ super().__init__(PadOp, [node_A], ctx) self.paddings = paddings self.mode = mode self.constant_values = constant_values def compute(self, input_vals, output_val, stream_handle=None): if self.on_cpu: if DNNL_LIB['cpu_Pad']: cpu_pad(input_vals[0], output_val, self.paddings, self.mode, constant_values=0) else: output_val[:] = pad_np(input_vals[0].asnumpy( ), self.paddings, self.mode, constant_values=0) else: pad(input_vals[0], output_val, self.paddings, self.mode, self.constant_values, stream_handle) def gradient(self, output_grad): return [pad_gradient_op(output_grad, self.paddings, self.mode, ctx=self.raw_ctx)] def infer_shape(self, input_shapes): assert len(input_shapes) == 1 out_shape = list(input_shapes[0]) pad_len = len(self.paddings) for i in range(4): if(i - (4 - pad_len) >= 0): out_shape[i] = out_shape[i] + self.paddings[i - (4 - pad_len)][0] + self.paddings[i - (4 - pad_len)][1] return tuple(out_shape) class Pad_GradientOp(Op): def __init__(self, node_A, paddings, mode="CONSTANT", ctx=None): """Creates a node that represents np.sum(node_A, axis=0). Only support common-case axis=0 reduction for simplicity of gradient. """ super().__init__(Pad_GradientOp, [node_A], ctx) self.paddings = paddings self.mode = mode def compute(self, input_vals, output_val, stream_handle=None): if self.on_cpu: if DNNL_LIB['cpu_Pad_Gradient']: cpu_pad_gradient( input_vals[0], output_val, self.paddings, self.mode) else: output_val[:] = pad_np_gradient( input_vals[0].asnumpy(), self.paddings) else: pad_gradient(input_vals[0], output_val, self.paddings, self.mode, stream_handle) def gradient(self, output_grad): raise NotImplementedError def infer_shape(self, input_shapes): assert len(input_shapes) == 1 out_shape = list(input_shapes[0]) pad_len = len(self.paddings) for i in range(4): if(i - (4 - pad_len) >= 0): out_shape[i] = out_shape[i] - self.paddings[i - (4 - pad_len)][0] - self.paddings[i - (4 - pad_len)][1] return tuple(out_shape) def pad_op(node_A, paddings, mode="CONSTANT", constant_values=0, ctx=None): """Pad an input variable. Parameters: ---- node_A : Node The Node to be padded. paddings : Node padding edge mode : CONSTANT/REFLECT/SYMMETRIC constant_values: scalar value padding values Returns: ---- A new Node instance created by Op. """ return PadOp(node_A, paddings, mode, constant_values, ctx=ctx) def pad_gradient_op(node_A, paddings, mode="CONSTANT", ctx=None): """Gradient node of pad operation. Parameters: ---- node_A : Node The Node to be padded. paddings : Node padding edge mode : CONSTANT/REFLECT/SYMMETRIC Returns: ---- A new Node instance created by Op. """ return Pad_GradientOp(node_A, paddings, mode, ctx=ctx) def pad_np(node_A, paddings, mode="constant", constant_values=0): import numpy as np return np.pad(node_A, paddings, mode=mode.lower(), constant_values=(constant_values, constant_values)) def pad_np_gradient(grad, paddings): slices = [] for c in paddings: e = None if c[1] == 0 else -c[1] slices.append(slice(c[0], e)) return grad[tuple(slices)]