U
    d<                     @   st   d dl Z d dlZd dlmZmZmZ d dlmZ dd Z	dd Z
dd	 ZdddZG dd deZG dd dZdS )    N)core	workspace
net_drawer)
caffe2_pb2c                 C   s   t j| dd | jD S )Nc                 S   s   g | ]}|d  qS )_grad ).0sr   r   B/tmp/pip-unpacked-wheel-ua33x9lu/caffe2/python/gradient_checker.py
<listcomp>   s     z$getGradientForOp.<locals>.<listcomp>)r   ZGradientRegistryZGetGradientForOpoutput)opr   r   r
   getGradientForOp   s     r   c                 C   s^   | | }t |tjrtj| S t |tjs.td}td|j|j	|g|}t
| tj| S )NZtmp_dense_gradZSparseToDense)
isinstancer   ZBlobReferencer   blobsGradientSliceAssertionErrorCreateOperatorindicesvaluesRunOperatorOnce)grad_mapinput_to_checkZ	grad_blobZ
dense_gradsparse_to_dense_opr   r   r
   _get_grad_blob   s    

r   c                    s   |  |  d }|| |p"i  D ]\}}|tj|< q(|D ],}| ksZtd|t|tjks@tq@t	| dd |D }	 fdd|D }
|	|
|fS )NZ_copyz.{} has no gradient, cannot check net gradient.c                 S   s   g | ]}|t j| fqS r   r   r   r   r   r   r   r
   r   6   s     z_get_grad.<locals>.<listcomp>c                    s   i | ]}|t  |qS r   )r   )r   r   r   r   r
   
<dictcomp>7   s    z_get_grad.<locals>.<dictcomp>)
ZCloneNameZAddGradientOperatorsitemsr   r   r   formatstr
RunNetOnce)netoutputsoutputs_with_gradinput_valuesinputs_with_gradsZgrad_netnamevaluer   Zforward_resultsZgradsr   r   r
   	_get_grad(   s"    



r+    c                 C   s:   t jj| ||||d t | |  }t |t|fS )N)atolrtolerr_msg)npZtestingZassert_allcloseabsflattenZmeanmax)Zvalue1Zvalue2	thresholdr/   deltar   r   r
   _assert_close=   s      r6   c                   @   s(   e Zd ZedddZedd	d
ZdS )NetGradientCheckerNHz>Fc                    s  fdd |rdt | D ]J\}}t| }	tdt| |  d d}
|
|	 W 5 Q R X q fddt| |D }|rt| \}}}t |D ]J\}}t| }	tdt| |  d d}
|
|	 W 5 Q R X q|d	 \}}}|d
d  D ]\}}}t	|t	|ks t
t t||D ]0\}\\}}\}}t|||d|||d q.| | ksvt
| D ]$\}}t|| ||d|d q~q d S )Nc                    s    fddD S )Nc                    s   g | ]} | qS r   r   )r   inet_outputsr   r
   r   N   s     zWNetGradientChecker.CompareNets.<locals>._get_output_with_grad_names.<locals>.<listcomp>r   r:   )outputs_with_grad_idsr:   r
   _get_output_with_grad_namesM   s    zCNetGradientChecker.CompareNets.<locals>._get_output_with_grad_namesZcaffe2_net_forward_z.pngwbc                    s$   g | ]\}}t || |qS r   )r+   )r   r$   r;   )r=   r'   r(   r   r
   r   X   s    z2NetGradientChecker.CompareNets.<locals>.<listcomp>Zcaffe2_net_r      zVDifferent forward pass results for output id {}. Corresponding output blobs: {} and {})r/   z Different gradients for input {})	enumerater   ZGetPydotGraphZ
create_pngopenr"   r   writeziplenr   r6   r!   keysr    )netsr%   r<   r(   r'   r4   Zprint_net_imagesr9   r$   Zpngfresults_Zbackward_netsZfirst_net_resultsZfirst_net_gradsnet_results	net_gradsidxZblob1Zblob_value1Zblob2Zblob_value2blobZblob_grad_valuer   )r=   r'   r(   r<   r
   CompareNetsI   sV        
  zNetGradientChecker.CompareNets-C6?皙?Tc                    s   t | g g\}} | }	 fdd}
fdd}t }t jD ]8}|
|||}|
||| }|| | d |j|< qZd|  }|r|d|  7 }t	|	|||S )Nc                    s*   | t j< t   tdd D  S )Nc                 S   s   g | ]}t j| qS r   r   r   r   r   r
   r      s   z=NetGradientChecker.Check.<locals>.GetLoss.<locals>.<listcomp>)r   r   r#   sum)	new_value)full_netr   r&   r   r
   GetLoss   s
    

z)NetGradientChecker.Check.<locals>.GetLossc                    s"       }|j|   |7  < |S N)copyflat)dimr5   Zinput_value)r   r'   r   r
   GetValue   s    z*NetGradientChecker.Check.<locals>.GetValue   z'Error in gradient check for net_copy {}z: {})
r+   r0   
zeros_likerangesizerW   r!   r   ZProtor6   )r$   r&   r'   r   Z	step_sizer4   Z	print_netrJ   rK   Zanalytic_gradrT   rY   grad_estimaterX   pos_lossneg_lossr/   r   )rS   r   r'   r&   r
   Checkx   s*        
zNetGradientChecker.Check)Nr8   F)rO   rP   T)__name__
__module____qualname__staticmethodrN   ra   r   r   r   r
   r7   H   s       .    r7   c                   @   s4   e Zd ZdZdddZdd Zdd	d
Zdd ZdS )GradientCheckerzA gradient checker in Python.

    This is not the most efficient way to check gradients, as the Python
    interface will involve a lot of copies back and forth operations. Use at your
    own risk.
    Ngradient_checkc                 C   s:   || _ || _|pt | _|| _|d kr0i | _n|| _d S rU   )	_stepsize
_thresholdr   ZDeviceOption_device_option_workspace_name_input_device_options)selfZstepsizer4   device_optionZworkspace_nameinput_device_optionsr   r   r
   __init__   s    zGradientChecker.__init__c              	   C   sx  t t|D ]*}t|| || | j|| | j q|| }	t| d}
|D ]<}|j| }t	|}|
|d 
 7 }
t|d || j qR|
d }
t| t|tjrftdtj|	tjd tdtjdtjd tjd	|j|jd
 | jd}tjd	|j|jd
 | jd}tddd|jd
 |jd
 dgd}t| t| t| t	d}n
t	|}|
|fS )Ng        rZ   r   g       @zeros)dtypeonesr?   ZEnsureCPUOutputZ_cpu)rn   ZScatterWeightedSum)r\   rD   r   FeedBlobrl   getrj   r   r   	FetchBlobrQ   ZRunOperatorsOncer   r   r   r0   r[   float32rs   r   r   r   )rm   r   grad_opsinputsZinput_namesr   	grad_nameoutputs_with_gradsr9   xlossrL   r)   arrZ	gv_cpu_opZ	gi_cpu_opr   gradr   r   r
   GetLossAndGrad   s`     



       



zGradientChecker.GetLossAndGradFc              
   C   sl  t  }| j|kr t | jd |j| j |dkrBt|\}}	|pRt	|d }
t
|D ],\}}t |j| ||
|j| | j q\|	| }| ||||j|||\}}t|| }|j|jkrtd|j|j|r| || tddk}|| j}t|D ]}|sB|dkrB|d |k rB|j| |j|< q|| j|  | j7  < | ||||j|||\}}|| j|  | jd 8  < | ||||j|||\}}|| j|  | j7  < || | j d |j|< qtj||| j| jd	 }t|r@t|}td
 tt ||j| |j| gj! d}nd}| j|krbt "  t | |||fS )a  Checks the operator in a very simple fashion by stacking a sum of
        squares on the top.

        Inputs:
          op: the operator to be checked.
          inputs: the input data in numpy arrays.
          input_to_check: an index specifying which input blob we should
              check.
          outputs_with_grads: indices specifying which output blobs will we
              need to check gradients with. For these outputs, we will collect a
              squared sum and also feed in their gradients.
          grad_operator: the gradient operator. If not given, we will get the
              gradient operator from the gradient registry.
          input_device_options: an optional mapping from input names to
              DeviceOptions (to override the default DeviceOption)
          ensure_outputs_are_inferred: if set will assert that the gradient output
              shapes matches the inferred shapes
        Outputs:
          boolean: True if it passes, False if it does not pass.
        TNr   z5Mismatched gradient shapes: estimated ({}), grad ({})ZCAFFE2_FULL_GRAD_CHECK1   rZ   )r-   r.   z'Failed. [idx, grad, grad_estimate] are:F)#r   ZCurrentWorkspacerk   ZSwitchWorkspacern   ZCopyFromrj   r   r   ZInferOpBlobDevicesAsDictr@   rt   inputru   r   r0   r[   shape	Exceptionr!   _assertInferTensorChecksosgetenvr]   r\   rW   rh   iscloseri   anyZflatnonzeroprintZvstackTZResetWorkspace)rm   r   ry   r   r{   rx   ro   Zensure_outputs_are_inferredZold_ws_nameZg_inputrl   r9   r~   rz   r}   r   r^   Zfull_grad_checkZdims_to_checkZcurrent_dimr_   rI   r`   Zfail_matrL   retr   r   r
   CheckSimple   s    
        

              
"
zGradientChecker.CheckSimplec                 C   sf  t  }|j|g |j| tj|gdd\}}t }|D ]}||j q@|D ]}||krrt	d
|t|}	t|	j}
t|| }|
|krt	d
|
|t|	tjkr4|	jtdkrt jj}n^|	jtdkrt jj}nD|	jtdkr
t jj}n(|	jtdkr&t jj}nd	
tj}ntt|	}|| }||krVt	d

||qVd S )NT)Z
nets_protoz!expected output {} to be inferredz,Mismatched inferred shape: want({}), got({})Zfloat64rw   Zint32Zint64z
unknown {}z+Mismatched inferred type: want({}), got({}))r   ZNetDefr   extendr   ZInferShapesAndTypessetupdater   r   r!   rv   listr   typer0   Zndarrayrr   ZTensorProtoZDOUBLEFLOATZINT32ZINT64r"   )rm   r   rx   Ztmp_netZinferred_shapesZinferred_typesr%   Zgrad_opr   rM   Zcorrect_shapeZinferred_shapeZcorrect_typeZinferred_typer   r   r
   r   Y  sX    



 



 z(GradientChecker._assertInferTensorChecks)Nrg   N)NNF)rb   rc   rd   __doc__rp   r   r   r   r   r   r   r
   rf      s      
7   
srf   )r,   )r   Znumpyr0   Zcaffe2.pythonr   r   r   Zcaffe2.protor   r   r   r+   r6   objectr7   rf   r   r   r   r
   <module>   s   
T