Element-wise min of each of the input tensors (with Numpy-style broadcasting support).
| 3050 | |
| 3051 | # optimize min to support multi inputs |
| 3052 | class Min(Operator): |
| 3053 | """ |
| 3054 | Element-wise min of each of the input tensors (with Numpy-style |
| 3055 | broadcasting support). |
| 3056 | """ |
| 3057 | |
| 3058 | def __init__(self): |
| 3059 | super(Min, self).__init__() |
| 3060 | self.masks = [] |
| 3061 | |
| 3062 | def _min(self, a, b): |
| 3063 | """ |
| 3064 | Args: |
| 3065 | a (CTensor): First operand |
| 3066 | b (CTensor): Second operand |
| 3067 | Returns: |
| 3068 | CTensor, the output |
| 3069 | tuple of CTensor, mask tensor |
| 3070 | """ |
| 3071 | m = singa.__sub__(a, b) |
| 3072 | mask0 = singa.LEFloat(m, 0) |
| 3073 | mask1 = singa.GTFloat(m, 0) |
| 3074 | res = singa.__add__(singa.__mul__(mask0, a), singa.__mul__(mask1, b)) |
| 3075 | return res, (mask0, mask1) |
| 3076 | |
| 3077 | def forward(self, *x): |
| 3078 | """ |
| 3079 | Args: |
| 3080 | *x (a list of CTensor): List of tensors for max. |
| 3081 | Returns: |
| 3082 | CTensor, the output |
| 3083 | """ |
| 3084 | assert (len(x) > 0) |
| 3085 | self.l = len(x) |
| 3086 | if len(x) == 1: |
| 3087 | res, masks = self._min(x[0], x[0]) |
| 3088 | self.masks.append(masks) |
| 3089 | return x[0] |
| 3090 | res, masks = self._min(x[0], x[1]) |
| 3091 | self.masks.append(masks) |
| 3092 | for i in range(2, len(x)): |
| 3093 | res, masks = self._min(res, x[i]) |
| 3094 | self.masks.append(masks) |
| 3095 | return res |
| 3096 | |
| 3097 | def backward(self, dy): |
| 3098 | """ |
| 3099 | Args: |
| 3100 | dy (CTensor): the gradient tensor from upper operations |
| 3101 | Returns: |
| 3102 | a tuple for (*dx), dx is data for dL / dx. |
| 3103 | """ |
| 3104 | if self.l == 1: |
| 3105 | return self.masks[0][0] |
| 3106 | else: |
| 3107 | ret = [] |
| 3108 | cumulation = None |
| 3109 | for mask0, mask1 in self.masks[::-1]: |