# Path: bionlp/utils/utils.py
# def theano_logsumexp(x, axis=None):
#     """
#     Compute log(sum(exp(x), axis=axis) in a numerically stable
#     fashion.
#     Parameters
#     ----------
#     x : tensor_like
#         A Theano tensor (any dimension will do).
#     axis : int or symbolic integer scalar, or None
#         Axis over which to perform the summation. `None`, the
#         default, performs over all axes.
#     Returns
#     -------
#     result : ndarray or scalar
#         The result of the log(sum(exp(...))) operation.
#     """
#     xmax = T.max(x,axis=axis, keepdims=True)
#     xmax_ = T.max(x,axis=axis)
#     return xmax_ + T.log(T.exp(x - xmax).sum(axis=axis))


from bionlp.utils.utils import theano_logsumexp
import lasagne,sys
import logging,theano
import theano.tensor as T
        if mask_input==True:
            self.mask_input = True
        else:
            assert False,'Output without Mask is not yet Enabled'
        assert incomings.__len__()==3,'the CRF layer should have 3 inputs: unary_potential,pairwise_potential and mask'

    def get_output_shape_for(self, input_shapes):
        return input_shapes[0]

    def get_output_for(self,inputs,normalization=False,**kwargs):
        net_input = inputs[0]
        pairwise_sequence=inputs[1].dimshuffle(1,0,2)
        assert inputs.__len__()==3,'the CRF layer should have 3 inputs: unary_potential,pairwise_potential and mask'
        mask=inputs[2]
        pair_shape=pairwise_sequence.shape
        pairwise_sequence=T.reshape(pairwise_sequence,(pair_shape[0],pair_shape[1],self.num_labels,self.num_labels))
        if 'unary' in kwargs and kwargs['unary']==True:
            return net_input[0]


        unary_sequence = net_input.dimshuffle(1,0,2)    #Reshuffling the batched unary potential shape so that it can be used for word level iterations in theano.scan

        mask = mask.dimshuffle(1,0)
        def forward_scan1(unary_sequence,Wp,mask,forward_sm):
            forward_sm=forward_sm+unary_sequence*mask.dimshuffle(0,'x')
            forward_sm=theano_logsumexp(forward_sm.dimshuffle(0,1,'x')+mask.dimshuffle(0,'x','x')*Wp,1)
            return forward_sm

        def backward_scan1(unary_sequence,Wp,mask,forward_sm):
            forward_sm=forward_sm+unary_sequence*mask.dimshuffle(0,'x')

# Path: bionlp/utils/utils.py
# def theano_logsumexp(x, axis=None):
#     """
#     Compute log(sum(exp(x), axis=axis) in a numerically stable
#     fashion.
#     Parameters
#     ----------
#     x : tensor_like
#         A Theano tensor (any dimension will do).
#     axis : int or symbolic integer scalar, or None
#         Axis over which to perform the summation. `None`, the
#         default, performs over all axes.
#     Returns
#     -------
#     result : ndarray or scalar
#         The result of the log(sum(exp(...))) operation.
#     """
#     xmax = T.max(x,axis=axis, keepdims=True)
#     xmax_ = T.max(x,axis=axis)
#     return xmax_ + T.log(T.exp(x - xmax).sum(axis=axis))


# Path: bionlp/taggers/rnn_feature/networks/crf_dual_layer.py
from bionlp.utils.utils import theano_logsumexp
import lasagne,sys
import logging,theano
import theano.tensor as T

        if mask_input==True:
            self.mask_input = True
        else:
            assert False,'Output without Mask is not yet Enabled'
        assert incomings.__len__()==3,'the CRF layer should have 3 inputs: unary_potential,pairwise_potential and mask'

    def get_output_shape_for(self, input_shapes):
        return input_shapes[0]

    def get_output_for(self,inputs,normalization=False,**kwargs):
        net_input = inputs[0]
        pairwise_sequence=inputs[1].dimshuffle(1,0,2)
        assert inputs.__len__()==3,'the CRF layer should have 3 inputs: unary_potential,pairwise_potential and mask'
        mask=inputs[2]
        pair_shape=pairwise_sequence.shape
        pairwise_sequence=T.reshape(pairwise_sequence,(pair_shape[0],pair_shape[1],self.num_labels,self.num_labels))
        if 'unary' in kwargs and kwargs['unary']==True:
            return net_input[0]


        unary_sequence = net_input.dimshuffle(1,0,2)    #Reshuffling the batched unary potential shape so that it can be used for word level iterations in theano.scan

        mask = mask.dimshuffle(1,0)
        def forward_scan1(unary_sequence,Wp,mask,forward_sm):
            forward_sm=forward_sm+unary_sequence*mask.dimshuffle(0,'x')
            forward_sm=theano_logsumexp(forward_sm.dimshuffle(0,1,'x')+mask.dimshuffle(0,'x','x')*Wp,1)
            return forward_sm

        def backward_scan1(unary_sequence,Wp,mask,forward_sm):
            forward_sm=forward_sm+unary_sequence*mask.dimshuffle(0,'x')

            forward_sm=theano_logsumexp(forward_sm.dimshuffle(0,1,'x')+mask.dimshuffle(0,'x','x')*Wp.dimshuffle(0,2,1),1)