# 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)
# 
# class Output(Op, NoBPropMixin, NoFPropMixin):
#     def __call__(self, x):
#         self.inputs = [x]
#         return self
# 
#     def setup(self):
#         pass
# 
# class SplitMixin(object):
#     pass
# 
# class Unary(Op):
#     x = None
# 
#     def __call__(self, x):
#         x = _require_op(x)
#         self.x = x
#         if isinstance(x, Constant):
#             # Propagate constant.
#             self.setup()
#             self.fprop()
#             return Constant(self.array)
#         self.bpropable = x.bpropable
#         self.inputs = [x]
#         return self


import numpy as np
import cudarray as ca
from .base import Op, Output, SplitMixin, Unary
    def bprop(self):
        for i in range(self.n_splits):
            ca.copyto(self.x.grad_array[i], self.outputs[i].grad_array)


class VStack(Op):
    def __call__(self, *xs):
        self.n_sources = len(xs)
        self.inputs = xs
        return self

    def setup(self):
        shape = self.inputs[0].shape
        for expr in self.inputs:
            if shape != expr.shape:
                raise ValueError('shape mismatch: %s and %s'
                                 % (shape, expr.shape))
        self.shape = (self.n_sources,) + shape
        self.array = ca.zeros(self.shape)
        self.grad_array = ca.zeros(self.shape)

    def fprop(self):
        for i in range(self.n_sources):
            ca.copyto(self.array[i], self.inputs[i].array)

    def bprop(self):
        for i in range(self.n_sources):
            ca.copyto(self.inputs[i].grad_array, self.grad_array[i])



# 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)
# 
# class Output(Op, NoBPropMixin, NoFPropMixin):
#     def __call__(self, x):
#         self.inputs = [x]
#         return self
# 
#     def setup(self):
#         pass
# 
# class SplitMixin(object):
#     pass
# 
# class Unary(Op):
#     x = None
# 
#     def __call__(self, x):
#         x = _require_op(x)
#         self.x = x
#         if isinstance(x, Constant):
#             # Propagate constant.
#             self.setup()
#             self.fprop()
#             return Constant(self.array)
#         self.bpropable = x.bpropable
#         self.inputs = [x]
#         return self


# Path: deeppy/expr/array.py
import numpy as np
import cudarray as ca
from .base import Op, Output, SplitMixin, Unary

    def bprop(self):
        for i in range(self.n_splits):
            ca.copyto(self.x.grad_array[i], self.outputs[i].grad_array)


class VStack(Op):
    def __call__(self, *xs):
        self.n_sources = len(xs)
        self.inputs = xs
        return self

    def setup(self):
        shape = self.inputs[0].shape
        for expr in self.inputs:
            if shape != expr.shape:
                raise ValueError('shape mismatch: %s and %s'
                                 % (shape, expr.shape))
        self.shape = (self.n_sources,) + shape
        self.array = ca.zeros(self.shape)
        self.grad_array = ca.zeros(self.shape)

    def fprop(self):
        for i in range(self.n_sources):
            ca.copyto(self.array[i], self.inputs[i].array)

    def bprop(self):
        for i in range(self.n_sources):
            ca.copyto(self.inputs[i].grad_array, self.grad_array[i])



class Concatenate(Op):