U
    dp                     @   s   d dl mZ dd Zdd Zdd Zdd	 Zd
d Zdd Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zd d! Zd"S )#    )
caffe2_pb2c           $   
      sz  ddl m} t| \} }}t|t| jks6tdt| |\}}|}dd | jD }|dd }dd | jD }	|	dd }	 fd	d|D }
i }i }t|
|D ]0\}}|r|d
 }|||||< t	|||< qt|dkstdt
||\}}t|dkrg g fS g }g }g }i }|	D ]h} | }||kr~|| }|d }|d }|t|| |||< || || n
|d q"g }t }t }|D ]}dd |jD }dd |jD } |D ]h}!|!|krڐq||!d}"|"s||!d}"|"r$t	|"}"|"|kr||" |"||!< n
||! q||  q||7 }t| |||||||d}#||# t|# ||fS )z
    Generates gradient Do operator, given forward Do op and a list
    of gradient blobs corresponding to forward op's outputs
    Returns a gradient op and a list of blobs corresponding to input gradients
    r   BlobReferencez4Different number of gradient blobs and Do op outputsc                 S   s   g | ]}t |qS  str.0or   r   B/tmp/pip-unpacked-wheel-ua33x9lu/caffe2/python/control_ops_grad.py
<listcomp>&   s     z#gen_do_gradient.<locals>.<listcomp>Nc                 S   s   g | ]}t |qS r   r   r	   ir   r   r   r   (   s     c                    s   g | ]} | qS r   r   r   outer_to_inner_mapr   r   r   +   s     z/_DO_OPERATOR_INNER_GRAD_z$Empty initial gradient map for Do op_gradz/_DO_OPERATOR_INNER_GRAD_COPY_c                 S   s   g | ]}t |qS r   r   r   r   r   r   r      s     c                 S   s   g | ]}t |qS r   r   r   r   r   r   r      s     )fwd_opfwd_netgrad_opsinputsoutputsblob_bindingssaved_fwd_blobsworkspace_blob_name)caffe2.python.corer   _do_op_sanity_check_and_processlenoutputAssertionErrordedupe_g_outputinputzipr   _gen_subgradient_passappend_prepare_blob_copy_opsetgetaddupdate_prepare_gradient_do_op)$opg_outputr   subnetinner_to_outer_mapr   r   deduped_g_output	op_outputop_inputZordered_inner_output_blob_namesZbackward_pass_initial_grad_mapZinitial_grad_mapZinner_output_nameZouter_grad_output_nameZinner_grad_output_nameZinner_grad_opsZinner_grad_names_mapZgrad_copy_opsg_inputZnew_op_outputsZnew_blob_bindingsZouter_input_nameZinner_input_nameZinner_grad_input_nameZouter_grad_input_nameZnew_inner_grad_input_nameZnew_op_inputsZoverwritten_namesZsaved_local_blob_namesgrad_opgrad_op_inputgrad_op_outputZgrad_op_input_name
outer_namegradient_do_defr   r   r   gen_do_gradient   s    


 
* 




	
r8   c           	      C   s   g }g }i }t | j|D ]\}}|s0|| q||krH|||  q|| krh|||< || q|d | d }|| kstt }d|_|j	|g |j	|g || || |||< q||fS )N_Z_DEDUPCopy)
r"   r   r$   valuesr   r   OperatorDeftyper!   extend)	r+   r,   r   r/   init_grad_mapoutput_nameZ	grad_nameZdeduped_grad_nameZgrad_copy_opr   r   r   r       s,    



r    c              
      s  ddl m} | jdkstdt| jdks4tdt| jt|ksNtdt| |\}}|}i }dd | jD }t||D ]\}}|r~|||||< q~t|dkstd	t	| d
}	|	stdt|	j
dkr|	j
d jdkstd|	j
d }
t|
}d|ks|d rtdt|
jdks2td|
jd }t|	|\} }}|s\td|t| ||||| d7 }dd | jD } fdd|D }||fS )z+
    Generates gradient While operator
    r   r   ZWhilezExpected While opz'Expected at least one input in While opz7Different number of gradient blobs and While op outputsc                 S   s   g | ]}t |qS r   r   r   r   r   r   r      s     z&gen_while_gradient.<locals>.<listcomp>z'Empty initial gradient map for While oploop_netz Expected loop subnet in While op   Doz6Gradient While op requires single Do op as a loop bodyreuse_workspacezFGradient While op requires Do loop body op without reuse_workspace setz'Expected Do op with at least one outputr   z4Failed to get gradient net for loop body in While op)r   input_namesoutput_namesloop_grad_networkspace_blobr?   loop_grad_mapc                 S   s   g | ]}t |qS r   r   r   r   r   r   r      s     c                    s   g | ]}  |d qS Nr'   r   rI   r   r   r      s     )r   r   r=   r   r   r!   r   r    r"   _get_net_argumentr+   _get_do_arguments_gen_subnet_gradient_prepare_gradient_while_ops)r+   r,   r   r   r/   r?   r0   r@   grad_output_namerA   do_opZdo_argsrH   rG   Zloop_input_namesZloop_output_namesr1   r2   r   rL   r   gen_while_gradient   sT    
 


	rS   c                 C   s  t  }||  |jr&| jd7  _t  }d|_|j| t  }	d|	_ddlm}
m} |
d}|
d}|	|
 d }||| ||| | D ]T\}}t|}t|}||kr|| |kr|||| | |||||  q|	j|  |jd d = |j||	g |jd d = |jd d = |jt|d	gt|  |jd d = |j| d
|_dd | jD |g S )Nr   rA   cond_netr   )Netr   Zgradient_loop_cond_netZgradient_loop_cond_net_initz/condutf-8Tc                 S   s   g | ]}|qS r   r   r   r   r   r   r   0  s     z/_prepare_gradient_while_ops.<locals>.<listcomp>)r   r<   CopyFromnameArgumentnr   rU   r   ZNextScopedBlobNameZHasScopeitemsr   r:   ZProtoargr>   control_inputr!   encodelistr   is_gradient_opr+   )r   rE   rF   rG   rH   r?   rI   Zgradient_while_defZloop_net_argZcond_net_argrU   r   rT   Zcond_init_netZ	cond_blobZblobZinit_grad_blobZ	blob_nameZinit_grad_blob_namer   r   r   rP     sR    


  
rP   c                 C   s   | j dkstdi }| jD ]}|js(q|jdkrL|js@td|j|d< q|jdkrt|jsdtdt|j|d< q|jdkr|jstd|j|d< q|jd	kr|jstd
|j|d	< q|S )NrC   Expected Do opnetzExpected non empty net argumentrD   z+Expected non empty reuse_workspace argumentinner_blobsz'Expected non empty inner_blobs argumentouter_blobs_idxz+Expected non empty outer_blobs_idx argument)	r=   r   r]   rX   rZ   r   boolstringsints)rR   argsr]   r   r   r   rN   3  s$    




rN   c                     s  ddl m} | jdkstdt| jdks4tdt| jt|ksNtdt| |\}}|}i }dd | jD }d	d | jD }t||D ]\}}	|	r||	|||< qt|dkstd
i t	| d}
|
stdt
|
|\}}}|std t }t }i  d}t	| d}|rt
||\} }}|sFtd  D ]\}}|krʈ| }||kr||kr|| nd}||kr||< n,||kr||< nd| d | d | n||< qN|||@  }tfdd|D }t||}|r|j| nft|dkr~t }|| |jrN| jd7  _|jdd= |j| |jdd= |jdd= |||@  }t fdd|D }t| |}|j| t||B }||B }|d gt|t|d   }t| ||||d}fdd|D }||g |fS )z
    Generates gradient If operator, given forward If op and a list
    of gradient blobs corresponding to forward op's outputs
    Returns a gradient op and a list of blobs corresponding to input gradients
    r   r   ZIfzExpected If opz$Expected at least one input in If opz4Different number of gradient blobs and If op outputsc                 S   s   g | ]}t |qS r   r   r   r   r   r   r   Z  s     z#gen_if_gradient.<locals>.<listcomp>c                 S   s   g | ]}t |qS r   r   r   r   r   r   r   [  s     z$Empty initial gradient map for If opthen_netzExpected then subnet in If opz,Failed to get gradient net for then in If opNelse_netz,Failed to get gradient net for else in If opzUnexpected grad blob name z, c                    s   g | ]}|   kr|qS r   r;   r   )then_grad_mapr   r   r     s      Z_auto_else_zero_blobs_c                    s   g | ]}|   kr|qS r   rl   r   )else_grad_mapr   r   r     s      )r   rE   rF   then_grad_netelse_grad_netc                    s   g | ]}  |d qS rJ   rK   r   )grad_mapr   r   r     s     )r   r   r=   r   r   r!   r   r    r"   rM   rO   r)   r&   r\   _gen_grad_zero_init_opsr+   r>   r   NetDefrW   rX   external_inputexternal_outputr`   _prepare_gradient_if_op) r+   r,   r   r   r/   r?   r1   r0   r@   rQ   rj   ro   Zthen_input_namesZthen_output_namesZelse_input_namesZelse_output_namesrp   rk   Z	else_blobZelse_grad_blobZthen_grad_blobinit_grad_nameZthen_other_output_namesZthen_other_grad_output_namesZ	zero_thenZelse_other_output_namesZelse_other_grad_output_namesZ	zero_elserF   rE   gradient_if_defr2   r   )rn   rq   rm   r   gen_if_gradientH  s    












  

  ry   c           
      C   s   t | |\}}t }t }|D ]D}|jD ]}t||kr(|t| q(|jD ]}|t| qNqt }	|	|  |	j	r|	 j	d7  _	|	j
d d = |	j
| |	jd d = |	jd d = |	|||fS )Nr   )r#   r&   r!   r   r(   r   r   rs   rW   rX   r+   r>   rt   ru   )
r-   	init_gradr   grad_names_maprF   rE   r3   r4   r5   gradient_net_defr   r   r   rO     s*     


rO   c                 C   s<   | j D ]0}|jr|j|kr|js,td| |j  S qd S )Nz Expected non empty net argument )r]   rX   rZ   r   )r+   net_namer]   r   r   r   rM     s
    
rM   c                 C   s
   t | |S )zA wrapper for external call)rM   )r+   r}   r   r   r   getNetArgument  s    r~   c           	      C   sR   ddl m} || j}||\}}i }| D ]\}}t||t|< q0||fS )Nr   )IR)r   r   r+   ZGetBackwardPassr\   r   )	r-   rz   r   Z	subnet_irr   Zgrad_blob_mapr{   bgr   r   r   r#     s    
r#   c                 C   s  | j dkstdt| d}|s(tdd }d }| jD ]}|jr|jdkr|rVtd|jrjt|jdksrtdd	d
 |jD }|jr|jdkr|rtd|jrt|jdkstd|j}|r6|r6 qq6|std|stdt|t|kstdt|}t|t|ks tddd
 | j	D }t|dksFtd|d }|d d }dd
 | j
D }t|dkstd|d }	||	kstd|d d }t|}
t|
t|kstdt|}t|t|kstd|| }t|}t }i }i }t||D ]f\}}|dkr2|t|k s:td|| }||ks\td| d || |||< |||< qt|t|kstd||||	fS )NrC   rb   rc   zNo net argument found in Do oprd   zinner_blobs redefinitionr   z#Empty inner_blobs argument in Do opc                 S   s   g | ]}| d qS rV   )decode)r	   sr   r   r   r     s     z3_do_op_sanity_check_and_process.<locals>.<listcomp>re   zouter_blobs_idx redefinitionz'Empty outer_blobs_idx argument in Do opz&No inner_blobs argument found in Do opz*No outer_blobs_idx argument found in Do opzFArguments inner_blobs and outer_blobs_idx of different length in Do opz(Found duplicates in inner_blobs in Do opc                 S   s   g | ]}t |qS r   r   r   r   r   r   r     s     z Expected at least one input blobr   c                 S   s   g | ]}t |qS r   r   r   r   r   r   r     s     z!Expected at least one output blobz)Expected same input/output workspace blobz Found duplicates in Do op inputsz!Found duplicates in Do op outputsz*Outer blob index is out of bounds in Do opzReusage of outer blob name z	 in Do opz;Not all outer blob names are used in blob bindings in Do op)r=   r   rM   r]   rX   rg   r   rh   r&   r!   r   r"   r(   )r+   r-   rd   re   r]   Zall_inner_blobsr1   Zinput_workspace_blob_namer0   r   Zall_op_input_blob_namesZall_op_output_blob_namesZordered_outer_blob_namesZall_outer_blob_namesZused_outer_blob_namesr   r.   Z
inner_nameZouter_blob_idxr6   r   r   r   r     s    





r   c                 C   s.   t  }d|_|j| g |j|g |S )Nr:   )r   r<   r=   r!   r>   r   )	from_nameZto_nameZcopy_op_defr   r   r   r%   2  s
    r%   c                    s  t  }|| |jr&| jd7  _|jd d = |j| |jd d = |jd d = t  }	|	|  |	jrt	|	jdkr|	 jd7  _|	j
d d = |	j
| |	j
| |	jd d = |	j| |	j| t  }
d|
_|
j| ||   } fdd|D }t  }d|_|jdd |D  t  }d|_|j| t  }d	|_|jd
d |D  |	jd d = |	j|
|||g |	jd d = d|	_|	S )Nr   r   rc   c                    s   g | ]}  | qS r   )indexr	   r   r   Zordered_new_outer_namesr   r   r   Z  s   z+_prepare_gradient_do_op.<locals>.<listcomp>rd   c                 S   s   g | ]}| d qS r   r_   r   r   r   r   r   _  s     re   r   c                 S   s   g | ]}| d qS r   r   r   r   r   r   r   h  s     T)r   rs   rW   rX   r+   r>   rt   ru   r<   r   r!   r$   r   rY   rZ   keysrg   rh   r]   r^   ra   )r   r   r   r   r   r   r   r   r|   r7   Znet_argrd   Znew_outer_blobs_idxZinner_blobs_argZouter_blobs_idx_argZsaved_blobs_argr   r   r   r*   :  s^    

   r*   c                 C   s   g }|D ]}d }|  D ]\}}||kr|} q2q|sBtd| d }|| kr| | }	|	|krt }d|_|jt|	g |jt|g nLt }d|_|j|g |j|g t	 }
d|
_
d|
_|j|
g |r|| q|S )NzUnknown gradient output r:   ZConstantFillvalueg        )r\   r   r   r<   r=   r!   r>   r   r   rY   rX   fr]   r$   )r?   rq   Zgrad_output_namesZgrad_init_opsZgrad_outputr@   r
   r   Zgrad_init_oprw   Z	value_argr   r   r   rr   t  s6    rr   c           	      C   s   t  }||  |jd d = |j| |jd d = |j| t  }d|_|j| |g}|rt  }d|_|j| |	| |j
d d = |j
| |jr| jd7  _|jd d = d|_|S )Nrj   rk   r   T)r   r<   rW   r!   r>   r   rY   rX   rZ   r$   r]   r^   ra   )	r   rE   rF   ro   rp   rx   Zthen_net_argZgradient_argsZelse_net_argr   r   r   rv     s,    

rv   c           	      C   s   t | d}| j| }|jD ]*}t|jD ]\}}||kr(||j|< q(qt | d}|r|jD ]*}t|jD ]\}}||krh||j|< qhqZ|| j|< d S )Nrj   rk   )rM   r   r+   	enumerate)	r3   idxZnew_grad_outputrj   Zold_grad_out_matchr+   r   outrk   r   r   r   disambiguate_grad_if_op_output  s    




r   N)Zcaffe2.protor   r8   r    rS   rP   rN   ry   rO   rM   r~   r#   r   r%   r*   rr   rv   r   r   r   r   r   <module>   s"     <.nK:%