# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # pylint: disable=invalid-name, unused-variable """NN operator common utilities""" import tvm from ..utils import get_const_int def infer_pad(data, data_pad): """Infer the padding from stages in reverse. Parameters ---------- data : Tensor data stage. data_pad : Tensor pad stage. Returns ------- hpad : int padding size on height wpad : int padding size on width """ if data_pad is None: return 0, 0 _, _, IH, IW = data.shape _, _, TH, TW = data_pad.shape hpad = (TH - IH) // 2 wpad = (TW - IW) // 2 return get_const_int(hpad), get_const_int(wpad) def infer_pad3d(data, data_pad, layout): """Infer the padding from stages in reverse. Parameters ---------- data : Tensor data stage. data_pad : Tensor pad stage. Returns ------- dpad : int padding depth hpad : int padding height wpad : int padding width """ if data_pad is None: return 0, 0, 0 if layout == "NDHWC": _, ID, IH, IW, _ = data.shape _, TD, TH, TW, _ = data_pad.shape elif layout == "NCDHW": _, _, ID, IH, IW = data.shape _, _, TD, TH, TW = data_pad.shape else: raise ValueError(f"Layout {layout} is not supported") dpad = TD - ID hpad = TH - IH wpad = TW - IW return get_const_int(dpad), get_const_int(hpad), get_const_int(wpad) def infer_stride(data, kernel, out): """Infer the stride from stages in reverse. Parameters ---------- data : Tensor data stage. kernel : Tensor kernel stage. out : Tensor output stage. Returns ------- hstride : int stride size on height wstride : int stride size on width """ _, _, IH, IW = data.shape _, _, KH, KW = kernel.shape _, _, OH, OW = out.shape hstride = (IH - KH) // tvm.te.max(OH - 1, 1) + tvm.tirx.Select(OH == 1, 1, 0) wstride = (IW - KW) // tvm.te.max(OW - 1, 1) + tvm.tirx.Select(OW == 1, 1, 0) return get_const_int(hstride), get_const_int(wstride) def get_pad_tuple(padding, kernel): """Common code to get the pad option Parameters ---------- padding : int or str Padding size, or ['VALID', 'SAME'] kernel : tuple of int Conv kernel size Returns ------- pad_top : int Padding size on top pad_left : int Padding size on left pad_down : int Padding size on down. pad_right : int Padding size on right. """ # compute the padding size if isinstance(padding, tuple | list): if len(padding) == 2: pad_h = padding[0] * 2 pad_w = padding[1] * 2 elif len(padding) == 4: return padding[0], padding[1], padding[2], padding[3] else: raise ValueError("Size of padding can only be 2 or 4") elif isinstance(padding, int): pad_h = pad_w = padding * 2 elif padding == "VALID": pad_h = 0 pad_w = 0 elif padding == "SAME": pad_h = kernel[0] - 1 pad_w = kernel[1] - 1 else: raise ValueError(f"Unknown padding option {padding}") pad_top = (pad_h + 1) // 2 pad_left = (pad_w + 1) // 2 return pad_top, pad_left, pad_h - pad_top, pad_w - pad_left def get_pad_tuple_generic(padding, kernel): """Common code to get the pad option Parameters ---------- padding : int or str Padding size, or ['VALID', 'SAME'] kernel : tuple of int Conv kernel size Returns ------- pad_top : int Padding size on top pad_down : int Padding size on down. pad_left : int Padding size on left pad_right : int Padding size on right. """ # compute the padding size if isinstance(padding, tuple | list): if len(padding) == len(kernel): pad_dimensions = [p * 2 for p in padding] elif len(padding) == len(kernel) * 2: return ( [padding[i] for i in range(len(kernel))], [padding[len(kernel) + i] for i in range(len(kernel))], ) else: raise ValueError("Size of padding can only be len(kernel) or len(kernel) * 2") elif isinstance(padding, int): pad_dimensions = [padding * 2 for _ in range(len(kernel))] elif padding == "VALID": pad_dimensions = [0 for _ in range(len(kernel))] elif padding == "SAME": pad_dimensions = [k - 1 for k in kernel] else: raise ValueError(f"Unknown padding option {padding}") pad_begin = [(p + 1) // 2 for p in pad_dimensions] return [pad_begin, [pd - pb for pb, pd in zip(pad_begin, pad_dimensions)]] def get_pad_tuple3d(padding, kernel): """Common code to get the pad option Parameters ---------- padding : int or str Padding size, or ['VALID', 'SAME'] kernel : tuple of int Conv kernel size Returns ------- pad_front : int Padding size on front. pad_top : int Padding size on top pad_left : int Padding size on left pad_back : int Padding size on back. pad_down : int Padding size on down. pad_right : int Padding size on right. """ # compute the padding size if isinstance(padding, tuple | list): if len(padding) == 3: pad_d = padding[0] * 2 pad_h = padding[1] * 2 pad_w = padding[2] * 2 elif len(padding) == 6: return padding[0], padding[1], padding[2], padding[3], padding[4], padding[5] else: raise ValueError("Size of padding can only be 3 or 6") elif isinstance(padding, int): pad_d = pad_w = pad_h = padding * 2 elif padding == "VALID": pad_h = 0 pad_w = 0 pad_d = 0 elif padding == "SAME": pad_d = kernel[0] - 1 pad_h = kernel[1] - 1 pad_w = kernel[2] - 1 else: raise ValueError(f"Unknown padding option {padding}") pad_top = (pad_h + 1) // 2 pad_left = (pad_w + 1) // 2 pad_front = (pad_d + 1) // 2 return pad_front, pad_top, pad_left, pad_d - pad_front, pad_h - pad_top, pad_w - pad_left def get_pad_tuple1d(padding, kernel): """Common code to get the pad option Parameters ---------- padding : int or str Padding size, or ['VALID', 'SAME'] kernel : tuple of int Conv kernel size Returns ------- pad_left : int Padding size on left pad_right : int Padding size on right. """ # compute the padding size if isinstance(padding, tuple | list): if len(padding) == 1: pad_w = padding[0] * 2 elif len(padding) == 2: return padding[0], padding[1] else: raise ValueError("Size of padding can only be 2 or 4") elif isinstance(padding, int): pad_w = padding * 2 elif padding == "VALID": pad_w = 0 elif padding == "SAME": pad_w = kernel[0] - 1 else: raise ValueError(f"Unknown padding option {padding}") pad_left = (pad_w + 1) // 2 return pad_left, pad_w - pad_left