U
    d=                     @   s   d dl Z d dlmZmZ d dlZd dlZd dlmZ G dd dejjZ	G dd dejjZ
G dd	 d	ejjZG d
d dejjZdS )    N)OptionalTuple)Tensorc                       s   e Zd ZdZejjZdeee	dd fddZ
deeeeef  eeef ddd	Zdee	eeef dddZdd ZedddZedd Z  ZS )LSTMCella  A quantizable long short-term memory (LSTM) cell.

    For the description and the argument types, please, refer to :class:`~torch.nn.LSTMCell`

    Examples::

        >>> import torch.nn.quantizable as nnqa
        >>> rnn = nnqa.LSTMCell(10, 20)
        >>> input = torch.randn(3, 10)
        >>> hx = torch.randn(3, 20)
        >>> cx = torch.randn(3, 20)
        >>> output = []
        >>> for i in range(6):
                hx, cx = rnn(input[i], (hx, cx))
                output.append(hx)
    TN	input_dim
hidden_dimbiasreturnc                    s   ||d}t    || _|| _|| _tjj|d| fd|i|| _tjj|d| fd|i|| _	tjj
 | _tjj
 | _tjj
 | _tjj
 | _tjj
 | _d S )Ndevicedtype   r	   )super__init__
input_sizehidden_sizer	   torchnnZLinearigateshgatesZ	quantizedZFloatFunctionalgatesfgate_cxigate_cgatefgate_cx_igate_cgateogate_cyselfr   r   r	   r   r   factory_kwargs	__class__ D/tmp/pip-unpacked-wheel-ua33x9lu/torch/nn/quantizable/modules/rnn.pyr       s    

  zLSTMCell.__init__)xhiddenr
   c                 C   s   |d ks |d d ks |d d kr4|  |jd |j}|\}}| |}| |}| j||}|dd\}}	}
}t	|}t	|	}	t
|
}
t	|}| j|	|}| j||
}| j||}|}t
|}| j||}||fS )Nr      r   )initialize_hiddenshapeis_quantizedr   r   r   addchunkr   Zsigmoidtanhr   mulr   r   r   )r   r#   r$   hxcxr   r   r   Z
input_gateZforget_gateZ	cell_gateZout_gater   r   r   cyZtanh_cyZhyr!   r!   r"   forward2   s$     






zLSTMCell.forwardF)
batch_sizer(   r
   c                 C   sV   t || jft || jf }}|rNt j|ddt jd}t j|ddt jd}||fS )N      ?r   ZscaleZ
zero_pointr   )r   zerosr   quantize_per_tensorZquint8)r   r1   r(   hcr!   r!   r"   r&   K   s
    "zLSTMCell.initialize_hiddenc                 C   s   dS )NZQuantizableLSTMCellr!   r   r!   r!   r"   	_get_nameR   s    zLSTMCell._get_namec                 C   s   |dk|dkkst |jd }|jd }| |||dk	d}tj||j_|dk	rbtj||j_tj||j_|dk	rtj||j_|S )zUses the weights and biases to create a new LSTM cell.

        Args:
            wi, wh: Weights for the input and hidden layers
            bi, bh: Biases for the input and hidden layers
        Nr%   )r   r   r	   )	AssertionErrorr'   r   r   	Parameterr   Zweightr	   r   )clswiwhbibhr   r   cellr!   r!   r"   from_paramsU   s    

zLSTMCell.from_paramsc                 C   s\   t || jkstt|ds$td| |j|j|j|j}|j	|_	|j	|j
_	|j	|j_	|S )Nqconfigz$The float module must have 'qconfig')type_FLOAT_MODULEr:   hasattrrB   Z	weight_ihZ	weight_hhZbias_ihZbias_hhrC   r   r   )r<   otherobservedr!   r!   r"   
from_floatj   s     

zLSTMCell.from_float)TNN)N)F)NN)__name__
__module____qualname____doc__r   r   r   rE   intboolr   r   r   r   r0   r&   r9   classmethodrB   rI   __classcell__r!   r!   r   r"   r      s       (r   c                       sX   e Zd ZdZdeeedd fddZdeee	eef  ddd	Z
ed
d Z  ZS )_LSTMSingleLayerzA single one-directional LSTM layer.

    The difference between a layer and a cell is that the layer can process a
    sequence, while the cell only expects an instantaneous value.
    TNr   c                    s0   ||d}t    t||fd|i|| _d S Nr   r	   )r   r   r   rA   r   r   r!   r"   r   |   s    

z_LSTMSingleLayer.__init__r#   r$   c                 C   s<   g }|D ]}|  ||}||d  qt|d}||fS )Nr   )rA   appendr   stack)r   r#   r$   resultZxxZresult_tensorr!   r!   r"   r0      s    z_LSTMSingleLayer.forwardc                 O   s(   t j||}| |j|j|j}||_|S )N)r   rB   r   r   r	   rA   )r<   argskwargsrA   layerr!   r!   r"   rB      s    z_LSTMSingleLayer.from_params)TNN)N)rJ   rK   rL   rM   rN   rO   r   r   r   r   r0   rP   rB   rQ   r!   r!   r   r"   rR   v   s       rR   c                       s^   e Zd ZdZdeeeeedd fddZdeee	eef  dd	d
Z
edddZ  ZS )
_LSTMLayerz#A single bi-directional LSTM layer.TFN)r   r   r	   batch_firstbidirectionalr
   c           	         sZ   ||d}t    || _|| _t||fd|i|| _| jrVt||fd|i|| _d S rS   )r   r   r\   r]   rR   layer_fwlayer_bw)	r   r   r   r	   r\   r]   r   r   r   r   r!   r"   r      s    

z_LSTMLayer.__init__rT   c                 C   s  | j r|dd}|d kr$d\}}n|\}}| jrv|d kr@d }n|d }|d }|d kr^d }n|d }|d }||f}|d kr|d krd }ntj|tj|f}| ||\}	}t| dr| jr|d}
| 	|
|\}}|d}t
|	|g|	 d }|d kr"|d kr"d }d }nh|d kr>|d }|d }nL|d krZ|d }|d }n0t|d |d gd}t|d |d gd}n|	}tj|\}}| j r|dd |||ffS )Nr   r%   )NNr_   )r\   	transposer]   r   jit_unwrap_optionalr^   rF   Zflipr_   catZdimrV   Z
transpose_)r   r#   r$   Zhx_fwZcx_fwZhx_bwZcx_bwZ	hidden_bwZ	hidden_fwZ	result_fwZ
x_reversedZ	result_bwrW   r6   r7   r!   r!   r"   r0      sP    






z_LSTMLayer.forwardr   c                 K   s:  t |ds|dk	st|d|j}|d|j}|d|j}|d|j}|d|j}	| |||||	}
t|d||
_	t|d| }t|d	| }t|d
| d}t|d| d}t
|||||
_|jr6t|d| d}t|d	| d}t|d
| dd}t|d| dd}t
|||||
_|
S )z
        There is no FP equivalent of this class. This function is here just to
        mimic the behavior of the `prepare` within the `torch.ao.quantization`
        flow.
        rC   Nr   r   r	   r\   r]   Zweight_ih_lZweight_hh_lZ	bias_ih_lZ	bias_hh_lZ_reverse)rF   r:   getr   r   r	   r\   r]   getattrrC   rR   rB   r^   r_   )r<   rG   Z	layer_idxrC   rY   r   r   r	   r\   r]   rZ   r=   r>   r?   r@   r!   r!   r"   rI      s(    z_LSTMLayer.from_float)TFFNN)N)r   N)rJ   rK   rL   rM   rN   rO   r   r   r   r   r0   rP   rI   rQ   r!   r!   r   r"   r[      s           4r[   c                
       s~   e Zd ZdZejjZdeeee	e	e
e	dd fdd	Zdeeeeef  d
ddZdd ZedddZedd Z  ZS )LSTMa7  A quantizable long short-term memory (LSTM).

    For the description and the argument types, please, refer to :class:`~torch.nn.LSTM`

    Attributes:
        layers : instances of the `_LSTMLayer`

    .. note::
        To access the weights and biases, you need to access them per layer.
        See examples below.

    Examples::

        >>> import torch.nn.quantizable as nnqa
        >>> rnn = nnqa.LSTM(10, 20, 2)
        >>> input = torch.randn(5, 3, 10)
        >>> h0 = torch.randn(2, 3, 20)
        >>> c0 = torch.randn(2, 3, 20)
        >>> output, (hn, cn) = rnn(input, (h0, c0))
        >>> # To get the weights:
        >>> print(rnn.layers[0].weight_ih)
        tensor([[...]])
        >>> print(rnn.layers[0].weight_hh)
        AssertionError: There is no reverse path in the non-bidirectional layer
    r%   TF        N)r   r   
num_layersr	   r\   dropoutr]   r
   c
                    s$  ||	d}
t    || _|| _|| _|| _|| _t|| _|| _	d| _
|rPdnd}t|tjrd|  krtdkrn n
t|trtd|dkrtd |dkrtd|| t| j| j| jfd| j	d	|
g}td|D ],}|t| j| j| jfd| j	d	|
 qtj|| _d S )
Nr   F   r%   r   zbdropout should be a number in range [0, 1] representing the probability of an element being zeroedz|dropout option for quantizable LSTM is ignored. If you are training, please, use nn.LSTM version followed by `prepare` step.zdropout option adds dropout after all but last recurrent layer, so non-zero dropout expects num_layers greater than 1, but got dropout={} and num_layers={})r\   r]   )r   r   r   r   rh   r	   r\   floatri   r]   Ztraining
isinstancenumbersNumberrO   
ValueErrorwarningswarnformatr[   rangerU   r   r   Z
ModuleListlayers)r   r   r   rh   r	   r\   ri   r]   r   r   r   num_directionsrt   rZ   r   r!   r"   r     sP    


$
 

zLSTM.__init__rT   c                    s  | j r|dd}|d}| jr&dnd}|d krtj||| jtj|jd	d |j
rptjdd|jdfddt| jD }nvtj|}t|d tr|d | j||| jd|d | j||| jd  fd	dt| jD }n|}t| jD ] \}}|||| \}||< q
g }	g }
t| jD ]*}|	|| d  |
|| d  q>t|	}t|
}|jd|jdd   }|jd|jdd   }| j r|dd}|||ffS )Nr   r%   rj   )r   r   r2   r3   c                    s   g | ]}  fqS r!   r!   ).0_)r4   r!   r"   
<listcomp>H  s     z LSTM.forward.<locals>.<listcomp>c                    s(   g | ] }|  d  |  d fqS )r   )squeeze_)rv   idx)r.   r-   r!   r"   rx   R  s     )r{   )r{   )r\   r`   sizer]   r   r4   r   rk   r   ry   r(   r5   r   rs   rh   ra   rb   rl   r   ZreshapeZunbind	enumeratert   rU   rV   r'   )r   r#   r$   Zmax_batch_sizeru   ZhxcxZhidden_non_optrz   rZ   Zhx_listZcx_listZ	hx_tensorZ	cx_tensorr!   )r.   r-   r4   r"   r0   :  s^    
 
 

zLSTM.forwardc                 C   s   dS )NZQuantizableLSTMr!   r8   r!   r!   r"   r9   k  s    zLSTM._get_namec                 C   s   t || jstt|ds"|s"t| |j|j|j|j|j|j	|j
}t|d||_t|jD ]}tj|||dd|j|< q\|  tjjj|dd}|S )NrC   F)r\   T)inplace)rl   rE   r:   rF   r   r   rh   r	   r\   ri   r]   re   rC   rs   r[   rI   rt   evalr   aoquantizationprepare)r<   rG   rC   rH   rz   r!   r!   r"   rI   n  s       
zLSTM.from_floatc                 C   s   t jjj|dddS )NFT)r   Zremove_qconfig)r   r   r   convert)r<   rG   r!   r!   r"   from_observed}  s    zLSTM.from_observed)r%   TFrg   FNN)N)N)rJ   rK   rL   rM   r   r   rf   rE   rN   rO   rk   r   r   r   r   r0   r9   rP   rI   r   rQ   r!   r!   r   r"   rf      s.               *1rf   )rm   typingr   r   rp   r   r   r   Moduler   rR   r[   rf   r!   r!   r!   r"   <module>   s   ib