U
    fJ                     @   s  d dl Zd dlZd dlmZ ddlmZ d dlmZ da	G dd deZ
dd	 Zd
d Zdd ZdgZdd Zg Zdd Zdd Zdd Zdd Zi Zeed< eed< eed< eed< eed< eed< eed< eed < eed!< eed"< eed#< eed$< eed%< eed&< eed'< eed(< eed)< eed*< eed+< eed,< eed-< eed.< eed/< eed0< eed1< eed2< eed3< eed4< eed< eed5< eed6< dS )7    N)	Explainer   )!standard_combine_mult_and_diffref)LooseVersionc                   @   sB   e Zd ZefddZdd Zdd Zdd Zd	d
 ZdddZ	dS )PyTorchDeepExplainerc           	   	   C   s  t dkr,ddl a tt jtdk r,td d| _t|drF|d}n|}t|tkr\|g}t|tkrnd| _t|tkr|g}|| _	|| _
d| _d| _d| _d| _d| _t|tkr>d| _|\}}| }|| _| | j t  @ || }| jj}t|tkrdd	 |D | _n
|jg| _W 5 Q R X | j  | j`| | _d| _d
| _t  \ || }|j| _|jd
 d
krd| _|jd
 | _t|ddkr|d  | _W 5 Q R X dS )a  
        Parameters
        ----------
        model : an nn.Module object (model), or a tuple (model, layer), where
            both are nn.Module objects.
            The model is an nn.Module object which takes as input a tensor (or list of tensors) of
            shape data, and returns an output of shape (B, 1), where B is the batch dimension.
            Note that the second dimension must be 1. Otherwise, the gradient cannot be computed
            (since the output won't be a scalar).
            If the input is a tuple, the returned shap values will be for the input of the
            layer argument. layer must be a layer in the model, i.e. model.conv2


        data : torch tensor or function  
            If a function is supplied, it must be a function that
            takes a particular input example and generates the background
            dataset for that example. When None is supplied as an input, 
            the function should return values that are in the same format
            as what it would return if an actual input were supplied.

        combine_mult_and_diffref : function
            This function determines how to combine the multipliers,
             the original input and the reference input to get
             the final attributions. Defaults to
             standard_combine_mult_and_diffref, which just multiplies
             the multipliers with the difference-from-reference (in
             accordance with the standard DeepLIFT formulation) and then
             averages the importance scores across the different references.
             However, different approaches may be applied depending on
             the use case (e.g. for computing hypothetical contributions
             in genomic data)
        Nr   z0.4z9Your PyTorch version is older than 0.4 and not supported.F__call__Tc                 S   s   g | ]
}|j qS  shape.0ir   r   H/tmp/pip-target-lpfmz8o1/lib/python/shap/explainers/deep/deep_pytorch.py
<listcomp>U   s     z1PyTorchDeepExplainer.__init__.<locals>.<listcomp>r   )torchr   __version__warningswarnmulti_inputhasattrtypelistdatacombine_mult_and_diffreflayerinput_handleinteriminterim_inputs_shapeZexpected_valuetupleevaladd_target_handleno_gradtarget_inputr
   target_handleremovemodelmulti_outputnum_outputsdeviceZmeancpunumpy)	selfr%   r   r   Zsample_datar   _interim_inputsoutputsr   r   r   __init__   s\    $






zPyTorchDeepExplainer.__init__c                 C   s   | t}|| _d S N)register_forward_hookget_target_inputr#   )r+   r   r   r   r   r   r    n   s    
z&PyTorchDeepExplainer.add_target_handlec                 C   s\   g }|  D ]J}dtt|kr6|| ||| q||| ||| q|S )zt
        Add handles to all non-container layers in the model.
        Recursively for non-container layers
        nn.modules.container)childrenstrr   extendadd_handlesappendr1   Zregister_backward_hook)r+   r%   Zforward_handleZbackward_handleZhandles_listchildr   r   r   r7   r   s    z PyTorchDeepExplainer.add_handlesc              	   C   sj   |  D ]\}dtt|kr(| | qz|`W n tk
rD   Y nX z|`W q tk
rb   Y qX qdS )z
        Removes the x and y attributes which were added by the forward handles
        Recursively searches for non-container layers
        r3   N)r4   r5   r   remove_attributesxAttributeErrory)r+   r%   r9   r   r   r   r:      s    z&PyTorchDeepExplainer.remove_attributesc                    s   | j   dd |D }| j | }dd |d d |f D  | jrt| jj} fdd|D }| j`|dd |D fS  fdd|D }|S d S )Nc                 S   s   g | ]}|  qS r   )Zrequires_grad_r   r;   r   r   r   r      s     z1PyTorchDeepExplainer.gradient.<locals>.<listcomp>c                 S   s   g | ]}|qS r   r   )r   valr   r   r   r      s     c                    s*   g | ]"}t jj |d dd   qS T)Zretain_graphr   r   Zautogradgradr)   r*   )r   inputselectedr   r   r      s     c                 S   s   g | ]}|    qS r   detachr)   r*   r   r   r   r   r      s     c                    s*   g | ]"}t jj |d dd   qS r@   rA   r>   rD   r   r   r      s     )r%   Z	zero_gradr   r   r"   )r+   idxinputsXr.   r-   gradsr   rD   r   gradient   s    

zPyTorchDeepExplainer.gradientNmaxc              
      s\  j s"t tkstd g nt tks6tdfdd D  |d k	rjrt  j  }W 5 Q R X |dkrtj|dd\}}nJ|dkrtj|d	d\}}n.|d
krtjt	|dd\}}nd	std|d d d |f }n0t
 d jd jf tdj  }jtt}jrDj g }	t|jd D ]}
g }jrttjD ]4}|t d jd fj| dd    qvn*tt D ]}|t | j qt d jd D ]|d k	r*| dkr*tdd d jd  tj  tj drp  fddtt D ttkrvgnj  fddtt D fddtt D }||
f }!||jrp\}g g  }}tt|D ].}
t"||
 d\}}|| || qj#fddttjD ||d}ttjD ]}|| || < qVnnj#fddtt D  fddtt D dd D d}tt D ]}|| || < qƐq|	j s|d n| qV|D ]}|$  q%j jr2j&$  jsB|	d S |d k	rT|	|fS |	S d S )Nz%Expected a single tensor model input!z Expected a list of model inputs!c                    s   g | ]}|  jqS r   )tor(   r>   )r+   r   r   r      s     z4PyTorchDeepExplainer.shap_values.<locals>.<listcomp>rM   T)Z
descendingminFZmax_absz/output_rank_order must be max, min, or max_abs!r   r   Donezexamples ofr   c                    s   g | ]} |  qS r   r   r   lrJ   jr   r   r      s     c                    sV   g | ]N} | d    | jd ftdd tt | jd  D  qS )r   r   c                 S   s   g | ]}d qS r   r   )r   kr   r   r   r      s     z?PyTorchDeepExplainer.shap_values.<locals>.<listcomp>.<listcomp>)repeatr
   r   rangelenrQ   )rJ   bg_datarT   r   r   r      s   2c                    s&   g | ]}t j|  | fd dqS )r   )dim)r   catrQ   )rZ   tiled_Xr   r   r      s        c                    s(   g | ] }| d  | j d   qS Nr   r	   rQ   rZ   sample_phisr   r   r      s   )ZmultZorig_inprZ   c                    s(   g | ] }| d  | j d   qS r_   r	   rQ   r`   r   r   r      s   c                    s$   g | ]} |      qS r   rF   rQ   rS   r   r   r      s   c                 S   s   g | ]}|    qS r   rF   r>   r   r   r   r      s     )'r   r   r   AssertionErrorr&   r   r!   r%   sortabsZonesr
   r'   intZaranger7   add_interim_valuesdeeplift_gradr   r    r   rX   rY   r   r8   npZzerosprintsysstdoutflushr   r   rL   splitr   r$   r:   r#   )r+   rJ   Zranked_outputsZoutput_rank_orderZprogress_messageZmodel_output_valuesr,   Zmodel_output_ranksZhandlesZoutput_phisr   ZphisrV   Zjoint_xZfeature_indoutputr;   r   Zx_tempZ	data_tempZphis_jrR   handler   )rJ   rZ   rT   ra   r+   r]   r   shap_values   s    
4

"






z PyTorchDeepExplainer.shap_values)NrM   N)
__name__
__module____qualname__r   r/   r    r7   r:   rL   rp   r   r   r   r   r   	   s   
c  r   c                 C   sJ   | j j}|tkr0t| jdkrFt| | ||S ntd|d  |S dS )zPThe backward hook which computes the deeplift
    gradient for an nn.Module
    )passthrough	linear_1dz#Warning: unrecognized nn.Module: {}z; using regular gradientsN)	__class__rq   
op_handlerri   format)module
grad_inputgrad_outputmodule_typer   r   r   rg     s    
rg   c                 C   sL  z| ` W n tk
r   Y nX z| `W n tk
r:   Y nX | jj}|tkrHt| j}|dkrbntt|D ]0}|dkrnt|t	krn|| || ksnt
dqn|dkr0t|t	krt| dtj|d   nt| dtj|  t|t	krt| dtj|d   nt| dtj|  |tkrH|d t dS )zrThe forward hook used to save interim tensors, detached
    from the graph. Used to calculate the multipliers
    rt   r   zOnly the 0th input may vary!)maxpoolnonlinear_1dr;   r=   N)r;   r<   r=   rv   rq   rw   rX   rY   r   r   rb   setattrr   nn	ParameterrG   failure_case_modulesregister_hookdeeplift_tensor_grad)ry   rC   rn   r|   	func_namer   r   r   r   rf   !  s2    



rf   c                 C   s.   z| ` W n tk
r   Y nX t| d| dS )zA forward hook which saves the tensor - attached to its graph.
    Used if we want to explain the interim outputs of a model
    r"   N)r"   r<   r   )ry   rC   rn   r   r   r   r2   H  s
    r2   	MaxPool1dc                 C   s   t d }t d= |S )N)complex_module_gradients)rB   Zreturn_gradr   r   r   r   ]  s    r   c                 C   s   dS )zNo change made to gradientsNr   ry   rz   r{   r   r   r   rt   f  s    rt   c                 C   s  t jjjt jjjt jjjd}t jjjt jjjt jjjd}| j	d t
| j	jd d  | j	t
| j	jd d d   }dgdd |jdd  D  }t | jd\}}t ||}	t |	| ||	 gd}
t  n || jj | j	| j| j| j| j| jd\}}t || jj |d |
 || j| j| jt| j	jd\}}W 5 Q R X dd |D }t t |d	k t ||| | ||d< | jjd
krt|d  t |d d| d|d< | `	| `t!|S )N)r   	MaxPool2d	MaxPool3dr   r^   c                 S   s   g | ]}d qS rU   r   r   r   r   r   r   w  s     zmaxpool.<locals>.<listcomp>r   Tc                 S   s   g | ]}d qS r0   r   r   r,   r   r   r   r     s     gHz>r   r   )"r   r   Z
functionalZmax_unpool1dZmax_unpool2dZmax_unpool3dZ
max_pool1dZ
max_pool2dZ
max_pool3dr;   re   r
   chunkr=   rM   r\   r!   rv   rq   Zkernel_sizeZstridepaddingZdilationZ	ceil_moder   whererd   Z
zeros_likerW   r   r8   gatherZ	unsqueezer   )ry   rz   r{   Zpool_to_unpoolZpool_to_functiondelta_indup0r=   Z
ref_outputZ	cross_maxZdiffsr,   indicesZxmax_posZrmax_posr   r   r   r}   k  sZ    <

     
    

r}   c                 C   s   dS )zNo change made to gradients.Nr   r   r   r   r   ru     s    ru   c                 C   s   | j d t| j jd d  | j t| j jd d d   }| jd t| jjd d  | jt| jjd d d   }dgdd |jdd  D  }dd |D }tt||dk |d |d || | |d< | `| ` t|S )Nr   r^   c                 S   s   g | ]}d qS rU   r   r   r   r   r   r     s     z nonlinear_1d.<locals>.<listcomp>r   c                 S   s   g | ]}d qS r0   r   r   r   r   r   r     s     gư>)	r=   re   r
   r;   r   r   rd   rW   r   )ry   rz   r{   Z	delta_outr   r   rK   r   r   r   r~     s    <<r~   Z	Dropout3dZ	Dropout2dZDropoutZAlphaDropoutZConv1dZConv2dZConv3dZConvTranspose1dZConvTranspose2dZConvTranspose3dZLinearZ	AvgPool1dZ	AvgPool2dZ	AvgPool3dZAdaptiveAvgPool1dZAdaptiveAvgPool2dZAdaptiveAvgPool3dZBatchNorm1dZBatchNorm2dZBatchNorm3dZ	LeakyReLUZReLUZ	ThresholdZELUZSigmoidZTanhZSoftplusZSoftmaxr   r   )r*   rh   r   Zshap.explainers.explainerr   miscr   Zdistutils.versionr   r   r   rg   rf   r2   r   r   r   rt   r}   ru   r~   rw   r   r   r   r   <module>   sd     
'&