# Path: deeppy/expr/base.py
# class Op(PickleMixin):
#     _pickle_ignore = ['_tmp_', 'array', 'grad_array']
#     inputs = []
#     bpropable = True
#     shape = None
#     array = None
#     grad_array = None
# 
#     def setup(self):
#         pass
# 
#     def fprop(self):
#         raise NotImplementedError()
# 
#     def bprop(self):
#         raise NotImplementedError()
# 
#     def __add__(self, other):
#         return deeppy.expr.Add()(self, other)
# 
#     def __radd__(self, other):
#         return deeppy.expr.Add()(other, self)
# 
#     def __iadd__(self, other):
#         return self.__add__(other)
# 
#     def __sub__(self, other):
#         return deeppy.expr.Subtract()(self, other)
# 
#     def __rsub__(self, other):
#         return deeppy.expr.Subtract()(other, self)
# 
#     def __isub__(self, other):
#         return self.__sub__(other)
# 
#     def __mul__(self, other):
#         return deeppy.expr.Multiply()(self, other)
# 
#     def __rmul__(self, other):
#         return deeppy.expr.Multiply()(other, self)
# 
#     def __imul__(self, other):
#         return self.__mul__(other)
# 
#     def __div__(self, other):
#         return deeppy.expr.Divide()(self, other)
# 
#     def __rdiv__(self, other):
#         return deeppy.expr.Divide()(other, self)
# 
#     def __idiv__(self, other):
#         return self.__div__(other)
# 
#     def __truediv__(self, other):
#         return deeppy.expr.Divide()(self, other)
# 
#     def __rtruediv__(self, other):
#         return deeppy.expr.Divide()(other, self)
# 
#     def __itruediv__(self, other):
#         return self.__truediv__(other)
# 
#     def __pow__(self, other):
#         return deeppy.expr.Power()(self, other)
# 
#     def __rpow__(self, other):
#         return deeppy.expr.Power()(other, self)
# 
#     def __ipow__(self, other):
#         return self.__pow__(other)
# 
#     def __neg__(self):
#         return deeppy.expr.Negative()(self)
# 
#     @property
#     def T(self):
#         return deeppy.expr.transpose(self)
# 
# Path: deeppy/expr/nnet/activation.py
# class Softmax(UnaryElementWise):
#     def fprop(self):
#         # e_i = exp(x_i - max(x))
#         # y = e_i / sum(e)
#         tmp1 = ca.amax(self.x.array, axis=1, keepdims=True)
#         ca.subtract(self.x.array, tmp1, self.array)
#         ca.exp(self.array, self.array)
#         ca.sum(self.array, axis=1, keepdims=True, out=tmp1)
#         self.array /= tmp1
# 
#     def bprop(self):
#         # y_i * (y_grad_i - sum(y_grad * y))
#         ca.multiply(self.array, self.grad_array, self.x.grad_array)
#         tmp1 = ca.sum(self.x.grad_array, axis=1, keepdims=True)
#         ca.subtract(self.grad_array, tmp1, self.x.grad_array)
#         self.x.grad_array *= self.array
# 
# Path: deeppy/expr/nnet/one_hot.py
# class OneHot(Unary):
#     def __init__(self, n_classes):
#         self.n_classes = n_classes
# 
#     def setup(self):
#         self.shape = self.x.shape + (self.n_classes,)
#         self.array = ca.zeros(self.shape)
# 
#     def fprop(self):
#         ca.nnet.one_hot_encode(self.x.array, self.n_classes, self.array)


import cudarray as ca
from ..base import Op
from .activation import Softmax
from .one_hot import OneHot
        ca.add(self.pred.array, self.eps, tmp2)
        ca.log(tmp2, tmp2)
        tmp2 *= self.target.array
        ca.add(tmp1, tmp2, tmp1)
        tmp1 *= -1
        ca.sum(tmp1, axis=1, out=self.array)

    def bprop(self):
        # -(target/pred - (1 - target)/(1 - pred))
        tmp1 = 1 - self.target.array
        tmp2 = 1 - self.pred.array
        tmp2 += self.eps
        ca.divide(tmp1, tmp2, tmp1)
        ca.add(self.pred.array, self.eps, tmp2)
        ca.divide(self.target.array, tmp2, out=tmp2)
        ca.subtract(tmp1, tmp2, self.pred.grad_array)
        self.pred.grad_array *= ca.reshape(self.grad_array, self.bcast_shape)


class _SoftmaxIdentityBProp(Softmax):
    def bprop(self):
        self.x.grad_array = self.grad_array


class SoftmaxCrossEntropy(Loss):
    def __init__(self, n_classes=None, eps=1e-8):
        self.n_classes = n_classes
        self.eps = eps

    def __call__(self, pred, target):

# Path: deeppy/expr/base.py
# class Op(PickleMixin):
#     _pickle_ignore = ['_tmp_', 'array', 'grad_array']
#     inputs = []
#     bpropable = True
#     shape = None
#     array = None
#     grad_array = None
# 
#     def setup(self):
#         pass
# 
#     def fprop(self):
#         raise NotImplementedError()
# 
#     def bprop(self):
#         raise NotImplementedError()
# 
#     def __add__(self, other):
#         return deeppy.expr.Add()(self, other)
# 
#     def __radd__(self, other):
#         return deeppy.expr.Add()(other, self)
# 
#     def __iadd__(self, other):
#         return self.__add__(other)
# 
#     def __sub__(self, other):
#         return deeppy.expr.Subtract()(self, other)
# 
#     def __rsub__(self, other):
#         return deeppy.expr.Subtract()(other, self)
# 
#     def __isub__(self, other):
#         return self.__sub__(other)
# 
#     def __mul__(self, other):
#         return deeppy.expr.Multiply()(self, other)
# 
#     def __rmul__(self, other):
#         return deeppy.expr.Multiply()(other, self)
# 
#     def __imul__(self, other):
#         return self.__mul__(other)
# 
#     def __div__(self, other):
#         return deeppy.expr.Divide()(self, other)
# 
#     def __rdiv__(self, other):
#         return deeppy.expr.Divide()(other, self)
# 
#     def __idiv__(self, other):
#         return self.__div__(other)
# 
#     def __truediv__(self, other):
#         return deeppy.expr.Divide()(self, other)
# 
#     def __rtruediv__(self, other):
#         return deeppy.expr.Divide()(other, self)
# 
#     def __itruediv__(self, other):
#         return self.__truediv__(other)
# 
#     def __pow__(self, other):
#         return deeppy.expr.Power()(self, other)
# 
#     def __rpow__(self, other):
#         return deeppy.expr.Power()(other, self)
# 
#     def __ipow__(self, other):
#         return self.__pow__(other)
# 
#     def __neg__(self):
#         return deeppy.expr.Negative()(self)
# 
#     @property
#     def T(self):
#         return deeppy.expr.transpose(self)
# 
# Path: deeppy/expr/nnet/activation.py
# class Softmax(UnaryElementWise):
#     def fprop(self):
#         # e_i = exp(x_i - max(x))
#         # y = e_i / sum(e)
#         tmp1 = ca.amax(self.x.array, axis=1, keepdims=True)
#         ca.subtract(self.x.array, tmp1, self.array)
#         ca.exp(self.array, self.array)
#         ca.sum(self.array, axis=1, keepdims=True, out=tmp1)
#         self.array /= tmp1
# 
#     def bprop(self):
#         # y_i * (y_grad_i - sum(y_grad * y))
#         ca.multiply(self.array, self.grad_array, self.x.grad_array)
#         tmp1 = ca.sum(self.x.grad_array, axis=1, keepdims=True)
#         ca.subtract(self.grad_array, tmp1, self.x.grad_array)
#         self.x.grad_array *= self.array
# 
# Path: deeppy/expr/nnet/one_hot.py
# class OneHot(Unary):
#     def __init__(self, n_classes):
#         self.n_classes = n_classes
# 
#     def setup(self):
#         self.shape = self.x.shape + (self.n_classes,)
#         self.array = ca.zeros(self.shape)
# 
#     def fprop(self):
#         ca.nnet.one_hot_encode(self.x.array, self.n_classes, self.array)


# Path: deeppy/expr/nnet/loss.py
import cudarray as ca
from ..base import Op
from .activation import Softmax
from .one_hot import OneHot

        ca.add(self.pred.array, self.eps, tmp2)
        ca.log(tmp2, tmp2)
        tmp2 *= self.target.array
        ca.add(tmp1, tmp2, tmp1)
        tmp1 *= -1
        ca.sum(tmp1, axis=1, out=self.array)

    def bprop(self):
        # -(target/pred - (1 - target)/(1 - pred))
        tmp1 = 1 - self.target.array
        tmp2 = 1 - self.pred.array
        tmp2 += self.eps
        ca.divide(tmp1, tmp2, tmp1)
        ca.add(self.pred.array, self.eps, tmp2)
        ca.divide(self.target.array, tmp2, out=tmp2)
        ca.subtract(tmp1, tmp2, self.pred.grad_array)
        self.pred.grad_array *= ca.reshape(self.grad_array, self.bcast_shape)


class _SoftmaxIdentityBProp(Softmax):
    def bprop(self):
        self.x.grad_array = self.grad_array


class SoftmaxCrossEntropy(Loss):
    def __init__(self, n_classes=None, eps=1e-8):
        self.n_classes = n_classes
        self.eps = eps

    def __call__(self, pred, target):

        if isinstance(pred, Softmax) and \