U
    Kc@?                     @   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(6, 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j | _tj
jj | _tj
jj | _tj
jj | _tj
jj | _d S )Ndevicedtype   r	   )super__init__
input_sizehidden_sizer	   torchnnZLinearigateshgatesaoZ	quantizedZFloatFunctionalgatesfgate_cxigate_cgatefgate_cx_igate_cgateogate_cyselfr   r   r	   r   r   factory_kwargs	__class__ G/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/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   r2   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;   hasattrrC   Z	weight_ihZ	weight_hhZbias_ihZbias_hhrD   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   rF   intboolr   r   r   r   r1   r'   r:   classmethodrC   rJ   __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   rB   r   r    r"   r#   r   |   s    

z_LSTMSingleLayer.__init__r$   r%   c                 C   s<   g }|D ]}|  ||}||d  qt|d}||fS )Nr   )rB   appendr   stack)r   r$   r%   resultZxxZresult_tensorr"   r"   r#   r1      s    z_LSTMSingleLayer.forwardc                 O   s(   t j||}| |j|j|j}||_|S )N)r   rC   r   r   r	   rB   )r=   argskwargsrB   layerr"   r"   r#   rC      s    z_LSTMSingleLayer.from_params)TNN)N)rK   rL   rM   rN   rO   rP   r   r   r   r   r1   rQ   rC   rR   r"   r"   r    r#   rS   v   s       rS   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 rT   )r   r   r]   r^   rS   layer_fwlayer_bw)	r   r   r   r	   r]   r^   r   r   r   r    r"   r#   r      s    

z_LSTMLayer.__init__rU   c                 C   s  | j r|dd}|d kr$d\}}n|\}}d }| jr|d krDd }n|d }|d }|d krbd }n|d }|d }|d k	r|d k	r||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r6|d kr6d }d }nh|d krRtj|\}}nL|d krntj|\}}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_   rG   Zflipr`   catZdimrW   Z
transpose_)r   r$   r%   Zhx_fwZcx_fwZ	hidden_bwZhx_bwZcx_bwZ	hidden_fwZ	result_fwZ
x_reversedZ	result_bwrX   r7   r8   r"   r"   r#   r1      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.
        rD   Nr   r   r	   r]   r^   Zweight_ih_lZweight_hh_lZ	bias_ih_lZ	bias_hh_lZ_reverse)rG   r;   getr   r   r	   r]   r^   getattrrD   rS   rC   r_   r`   )r=   rH   Z	layer_idxrD   rZ   r   r   r	   r]   r^   r[   r>   r?   r@   rA   r"   r"   r#   rJ      s(    z_LSTMLayer.from_float)TFFNN)N)r   N)rK   rL   rM   rN   rO   rP   r   r   r   r   r1   rQ   rJ   rR   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 )LSTMaU  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:
        >>> # xdoctest: +SKIP
        >>> 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   ri   r	   r]   floatrj   r^   Ztraining
isinstancenumbersNumberrP   
ValueErrorwarningswarnformatr\   rangerV   r   r   Z
ModuleListlayers)r   r   r   ri   r	   r]   rj   r^   r   r   r   num_directionsru   r[   r    r"   r#   r     sP    


$
 

zLSTM.__init__rU   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|}g }g }t| jD ]D\}	}
|
|||	 \}\}}|tj| |tj| qt|}t|}|d
|jd |jd
 }|d
|jd |jd
 }| j r|dd}|||ffS )Nr   r&   rk   )r   r   r3   r4   c                    s   g | ]}  fqS r"   r"   ).0_)r5   r"   r#   
<listcomp>I  s     z LSTM.forward.<locals>.<listcomp>c                    s(   g | ] }|  d  |  d fqS )r   )squeeze_)rw   idx)r/   r.   r"   r#   ry   S  s     )r]   ra   sizer^   r   r5   r   rl   r   rz   r)   r6   r   rt   ri   rb   rc   rm   r   ZreshapeZunbind	enumerateru   rV   rW   r(   )r   r$   r%   Zmax_batch_sizerv   ZhxcxZhidden_non_optZhx_listZcx_listr{   r[   r7   r8   Z	hx_tensorZ	cx_tensorr"   )r/   r.   r5   r#   r1   ;  s\    
 
 

zLSTM.forwardc                 C   s   dS )NZQuantizableLSTMr"   r9   r"   r"   r#   r:   j  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 )NrD   F)r]   T)Zinplace)rm   rF   r;   rG   r   r   ri   r	   r]   rj   r^   rf   rD   rt   r\   rJ   ru   evalr   r   Zquantizationprepare)r=   rH   rD   rI   r{   r"   r"   r#   rJ   m  s       
zLSTM.from_floatc                 C   s   t dd S )NzuIt looks like you are trying to convert a non-quantizable LSTM module. Please, see the examples on quantizable LSTMs.)NotImplementedError)r=   rH   r"   r"   r#   from_observed|  s    zLSTM.from_observed)r&   TFrh   FNN)N)N)rK   rL   rM   rN   r   r   rg   rF   rO   rP   rl   r   r   r   r   r1   r:   rQ   rJ   r   rR   r"   r"   r    r#   rg      s.               */rg   )rn   typingr   r   rq   r   r   r   Moduler   rS   r\   rg   r"   r"   r"   r#   <module>   s   ib