def subtract(left: Tensor, right: Tensor): left, right = coalesce(left, right) return Tensor.from_numpy( data=left.data - right.data, backward=lambda gradient: Gradients.accumulate( Gradient(tensor=left, gradient=gradient), Gradient(tensor=right, gradient=-gradient), ), )
def divide(left: Tensor, right: Tensor): left, right = coalesce(left, right) return Tensor.from_numpy( data=left.data / right.data, backward=lambda gradient: Gradients.accumulate( Gradient(tensor=left, gradient=gradient / right.data), Gradient( tensor=right, gradient=-gradient * left.data / np.square(right.data), ), ), )
def matrix_multiply(left: Tensor, right: Tensor): left = coalesce(left) right = coalesce(right) assert len(left.shape) == 2 assert len(right.shape) == 2 assert left.shape[1] == right.shape[0] return Tensor.from_numpy( data=np.matmul(left.data, right.data), backward=lambda gradient: Gradients.accumulate( Gradient(tensor=left, gradient=np.matmul(gradient, right.data.T)), Gradient(tensor=right, gradient=np.matmul(left.data.T, gradient)), ), )
def negate(tensor: Tensor): tensor = coalesce(tensor) return Tensor.from_numpy( data=-tensor.data, backward=lambda gradient: Gradients.accumulate( Gradient(tensor=tensor, gradient=-gradient)), )
def exp(tensor: Tensor): tensor = coalesce(tensor) return Tensor.from_numpy( data=np.exp(tensor.data), backward=lambda gradient: Gradients.accumulate( Gradient(tensor=tensor, gradient=gradient * np.exp(tensor.data))), )
def squeeze(tensor: Tensor, axes: Union[None, int, Tuple[int]] = None): tensor = coalesce(tensor) axes = () if axes is None else tuplify(axes) return Tensor.from_numpy( data=np.squeeze(tensor.data, axes), backward=lambda gradient: Gradients.accumulate( Gradient(tensor=tensor, gradient=np.expand_dims(gradient, axes))), )
def sum(tensor: Tensor, axes=None): tensor = coalesce(tensor) axes = tuplify(range(len(tensor.shape)) if axes is None else axes) return Tensor.from_numpy( data=tensor.data.sum(axes), backward=lambda gradient: Gradients.accumulate( Gradient( tensor=tensor, gradient=np.tile( np.expand_dims(gradient, axes), [ dim if idx in axes or idx - len(tensor.shape) in axes else 1 for idx, dim in enumerate(tensor.shape) ], ), ) ), )
def tile(tensor: Tensor, tiling: Tuple[int]): tensor = coalesce(tensor) tiling = tuple(tiling) assert len(tensor.shape) == len(tiling) return Tensor.from_numpy( data=np.tile(tensor.data, tiling), backward=lambda gradient: Gradients.accumulate( Gradient( tensor=tensor, gradient=np.reshape( gradient, [ value for idx, dim in enumerate(tensor.shape) for value in [tiling[idx], dim] ], ).sum(tuple(range(0, len(tiling) * 2, 2))), )), )
def backward(gradient: np.ndarray): result = gradient.copy() result[(tensor.data < low.data) | (tensor.data > high.data)] = 0 return Gradients.accumulate(Gradient(tensor=tensor, gradient=result))