U
    <cg                     @   sn  d dl Z d dlZd dlZd dlZd dlmZ d dlmZ d dlm	Z	 d dl
mZ d dlmZ d dlmZmZ d dlmZmZmZmZ dAee eed	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*d+ Z(d,d- Z)d.d/ Z*d0d1 Z+dBd5d6Z,d7d8 Z-d9d: Z.d;d< Z/dCd=d>Z0dDd?d@Z1dS )E    N)GraphDef)NodeDef)TensorShapeProto)bytes)
caffe2_pb2)core	workspace)SetDictTupleList)seennamemin_versionc                 C   sP   |dk	st |}|r d||f n|}|| krB|d7 }d||f }q$| | |S )a  
    Make the name unique by appending a unique number to the name. Used for SSA.

    Args:
        seen (set): Set of names that have already been used (with respect to
            some context).
        name (str): The name to make unique
        min_version (number): Starting index. Is incremented continually until
            it can make the resulting name unique relative to 'seen'.

    Returns:
        x (str): A version of name that is not in seen.
    Nz%s_%d   )AssertionErroradd)r   r   r   ix r   I/tmp/pip-unpacked-wheel-gikjz4vx/torch/utils/tensorboard/_caffe2_graph.py_make_unique_name   s    
r   c                    s   t d	t d
t dt dt d t dt dt dt d	t d
t d 	
fdd}t| ||| dS )a7  
    Convert some of the common names in Caffe2 to tensorflow.
    NOTE: The common names in both Caffe2 and Tensorflow are currently
        hardcoded, if either side changes at some point, then this code should
        change as well.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        blob_name_tracker: Dictionary of all unique blob names (with respect to
            some context).
        ops: List of Caffe2 operators

    Returns:
        None. The _rename_all() call modifies blob_name_tracker and ops in-place.
    z(_w)$z(_w_)z(_bn)$z(_bn_)z(_b)$z(_b_)z(_s)$z(_s_)z(_sum)$z(_sum_)z	(_branch)c                    st   
 d	 d| } d d|} d  d|} d d|} d	 d
|} d|}|S )Nz/weight_z/weightz/batchnorm_z
/batchnormz/bias_z/biasz/scale_z/scalez/sum_z/sumz/branch)sub)r   Z
inter_namenew_nameZBIASZBIAS_BNZBN_BRANCHZSCALEZSCALE_ZSUMZSUM_ZWEIGHTZWEIGHT_r   r   fE   s    z#_rename_tensorflow_style.<locals>.fN)recompile_rename_allshapesblob_name_trackeropsr   r   r   r   _rename_tensorflow_style)   s    










 	r%   c                    s   t |}t i i i ttttf td fddt||jD ]z\}|jksft	t
|j}t
|j}|jdd= |jdd= |jfdd|D  |jfdd|D  qP    r     dS )a  
    Convert an operator graph to SSA (i.e. out-of-place).
    i.e. blobs will be renamed so that each blob is produced only once.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        blob_name_tracker: Dictionary of all unique blob names (with respect to
            some context).
        ops: List of Caffe2 operators

    Returns:
        None. Modifies blob_name_tracker and ops in-place.
    )r   versionsreturnc                    sv   | |kst ||  }| |fkr,| |f S t| |d}|| |f< | krZ|  |<  rr|  krr |  |< |S )N)r   )r   r   )r   r&   versionr   )r#   new_blob_name_tracker
new_shapesr   r"   	versionedr   r   ssa_namee   s    z!_convert_to_ssa.<locals>.ssa_nameNc                 3   s   | ]}| j V  qd S N)Zin_versions.0r   ssar,   r   r   	<genexpr>}   s     z"_convert_to_ssa.<locals>.<genexpr>c                 3   s   | ]}| j V  qd S r-   )Zout_versionsr.   r0   r   r   r2   ~   s     )r   ZIRsetstrr
   intzipr1   opr   listinputoutputextendclearupdate)r"   r#   r$   Zirr7   inputsoutputsr   )r#   r)   r*   r   r"   r1   r,   r+   r   _convert_to_ssaQ   s&    
(


r@   c                 C   s6   t  }| D ]}||j ||j q
dd |D S )z
    Get all the operator input and output blobs and perform dedup on their names.

    Args:
        ops: List of Caffe2 operators to extract inputs and outputs from

    Returns:
        set containing distinct inputs and outputs from 'ops'
    c                 S   s   i | ]
}||qS r   r   r.   r   r   r   
<dictcomp>   s      z#_get_blob_names.<locals>.<dictcomp>)r3   r=   r9   r:   )r$   namesr7   r   r   r   _get_blob_names   s
    
rC   c                    s,    fdd|   D }|   | | dS )a  
    Rename keys of 'old_dict' according to 'rename_fn'.

    Args:
        old_dict: Dictionary (i.e. containing blob_name -> blob_name
            relationships.)
        remap_fn: Function string -> string for renaming.

    Returns:
        None. Modifies old_dict in-place.
    c                    s   i | ]\}} ||qS r   r   )r/   keyvalue	rename_fnr   r   rA      s      z_remap_keys.<locals>.<dictcomp>N)itemsr<   r=   )Zold_dictrG   Znew_dictr   rF   r   _remap_keys   s    rI   c                    s   t  i fdd |D ]d}t|j}t|j}|jdd= |jdd= |j fdd|D  |j fdd|D  qt|   |rt|      |D ]} |j|_qdS )a  
    Rename all the names in the operators.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        blob_name_tracker: Dictionary of all unique blob names (with respect to
            some context).
        ops: List of Caffe2 operators
        rename_fn: Function string -> string that specifies how to rename

    Returns:
        None. Modifies shapes, blob_name_tracker and ops in-place using the
            specified 'rename_fn'.
    c                    s6   | dkrdS | kr|  S t  | }|| < |S )zCollision-free version of f.N)r   )r   r   )rG   renamedr   r   r   g   s    z_rename_all.<locals>.gNc                 3   s   | ]} |V  qd S r-   r   r.   rK   r   r   r2      s     z_rename_all.<locals>.<genexpr>c                 3   s   | ]} |V  qd S r-   r   r.   rL   r   r   r2      s     )r3   r8   r9   r:   r;   rI   r<   r   )r"   r#   r$   rG   r7   r>   r?   r   )rK   rG   rJ   r   r   r       s"    




r    c                 C   s   dd }t | ||| dS )a  
    For all operators or blobs with name containing "_grad", add a
    "GRADIENTS/" scope.
    Note: breaks graph execution since the blob -> gradient mapping is
    hardcoded.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        blob_name_tracker: Dictionary of all unique blob names (with respect to
            some context).
        ops: List of Caffe2 operators

    Returns:
        None. Modifies shapes, blob_name_tracker and ops in-place by renaming.
    c                 S   s   d| krd | S | S d S )NZ_gradzGRADIENTS/{})formatr   r   r   r   r      s    
z_add_gradient_scope.<locals>.fNr    r!   r   r   r   _add_gradient_scope   s    rP   c                    s    fdd}t | ||| dS )a
  
    `:i` has a special meaning in Tensorflow. This function replaces all colons
    with $ to avoid any possible conflicts.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        blob_name_tracker: Dictionary of all unique blob names (with respect to
            some context).
        ops: List of Caffe2 operators
        repl: String representing the text to replace ':' with. Usually this is
            '$'.

    Returns:
        None. Modifies blob_name_tracker in-place.

    c                    s   |  d S )N:)replacerN   replr   r   r     s    z_replace_colons.<locals>.fNrO   )r"   r#   r$   rT   r   r   rS   r   _replace_colons   s    rU   c                 C   s   t  }| D ]}||j ||j q
| D ]l}|jr>|j}nF|jsJ|jr~dd |jpZ|jD }tj|}tj||j	}n|j	}|st
t|||_q,dS )a[  
    Give missing operators a name.
    We expect C2 operators to be generally unnamed. This gives them a scope
    (inferred from their outputs) and a name after their type. Duplicates will
    be postfixed by an index.

    Args:
        ops: List of Caffe2 operators to assign names to.

    Returns:
        None: Modifies 'ops' in-place.
    c                 S   s   g | ]}t j|qS r   )ospathdirnamer.   r   r   r   
<listcomp>   s     z0_fill_missing_operator_names.<locals>.<listcomp>N)r3   r=   r9   r:   r   rV   rW   commonprefixjointyper   r   )r$   r   r7   r   	name_listscoper   r   r   _fill_missing_operator_names
  s    r_   c                 C   sP   |  dsdS | jtjks&| jtjkr*dS | jtjkrBd| jS td| dS )a  
    Handle the devices.

    Args:
        device_option (caffe2_pb2.DeviceOption): DeviceOption protobuf,
            associated to an operator, that contains information such as
            device_type (optional), cuda_gpu_id (optional), node_name (optional,
            tells which node the operator should execute on). See caffe2.proto
            in caffe2/proto for the full list.

    Returns:
        Formatted string representing device information contained in
            device_option.
    device_type z/cpu:*z/gpu:{}zUnhandled deviceN)	HasFieldr`   r   ZCPUZMKLDNNZCUDArM   Z	device_id	Exceptiondevice_optionr   r   r   
_tf_device)  s    


rf   c                 C   sD   t  }|D ] }t  }||_|j|g q
| d jj|g dS )a;  
    Converts a list of ints to a TensorShapeProto representing the dimensions of
    a blob/object.

    Args:
        attr_dict: Dictionary to update (usually attributes of a Node)
        ints: List of integers representing dimensions of some object.

    Returns:
        None. Modifies attr_dict in-place.
    Z_output_shapesN)r   ZDimsizedimr;   r8   shape)	attr_dictintsZshape_protor   rh   r   r   r   _add_tf_shapeD  s    rl   c                 C   s  |j }|dkr$|jr$t| |j dS |dr>|j| | _dS |drX|j| | _dS |drt|jtrt|jnt	|j
d| | _dS |jr| | jj|j dS |jr| | jj|j dS |jr| | jjdd |jD  dS | | jjg  dS )	a;  
    Add attributes to a node. Key is the arg.name, and values can be shape,
        floats, strings, ints or an empty list.

    Args:
        attr_dict: Dictionary to update (usually attributes of a Node)
        arg: Object with name and data fields.

    Returns:
        None. Modifies attr_dict in-place.
    ri   Nr   r   sutf-8c                 s   s*   | ]"}t |tr|nt|d V  qdS )rn   N)
isinstancer   r4   encode)r/   rm   r   r   r   r2   }  s    z_set_tf_attr.<locals>.<genexpr>)r   rk   rl   rb   r   r   ro   rm   r   r4   rp   Zfloatsr8   r;   strings)rj   argkr   r   r   _set_tf_attrX  s4    


 rt   c                 C   s   |j st|t }|j |_ |j|j |j|_t|j|_	| rj|j
D ] }|| krX qjt|j| |  qH|jD ]}t|j| qp|S )a  
    Converts an operator to a node in a TF graph.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        op: The Caffe2 operator to convert to a TF graph node.

    Returns:
        n: The TF graph node created from op.
    )r   r   r   r9   r;   r\   r7   rf   re   devicer:   rl   attrrr   rt   )r"   r7   nr:   rr   r   r   r   _operator_to_node  s    

rx   c                    sx  | st g } fdd| jD }|| t|}|dkrt }|d |_|j| j | j|_	t
| j|_| jD ]}t|j| qr|| n|dkrt| jr| j}n$t|}	tj|	}
tj|
| j}|st t||| _t
| j}|D ]6}t }||_|j| jg d|_	||_|| qt }| j|_|j| j | j|_	||_| jD ]}t|j| qV|| |S )a"  
    Convert the operators to nodes.

    Args:
        op: Caffe2 operator to convert to node
        inter_blobs: Set of intermediate blobs
        seen: Names that have already been used and are not unique

    Returns:
        nodes: Nodes representing 'op' and the outputs of 'op'
    c                    s   g | ]}| kr|qS r   r   r/   ointer_blobsr   r   rY     s      z*_operator_to_node_simp.<locals>.<listcomp>r   r   Blob)r   r:   r=   lenr   r   r9   r;   r\   r7   rf   re   ru   rr   rt   rv   appendr8   rV   rW   rZ   r[   r   )r7   r|   r   Znodesr?   Zlen_outputsrw   rr   r   r]   r^   ru   r:   r   r{   r   _operator_to_node_simp  sN    






r   c                    s   |st t }||_| |g }t|dkr4d|_nd|_|jdd |D  |r|d d j t	 fdd|D rt
 |_|r||krt|j||  |S )a  
    Converts a blob (operator input or output) to a node in a TF graph.

    Args:
        producing_ops: Dictionary of blob name to list of
            (producing_op, blob_index within producing_op.output) mapping.
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        name: String representing the name of this blob.

    Returns:
        n: The TF graph node created from this blob.
    r   r}   ZPlaceholderc                 s   s    | ]\}}d |j |f V  qdS )z%s:%dNrN   )r/   Zp_opr   r   r   r   r2     s     z _blob_to_node.<locals>.<genexpr>c                 3   s   | ]}|d  j  kV  qdS )r   Nrd   )r/   Zproducerru   r   r   r2     s     )r   r   r   getr~   r7   r9   r;   re   allrf   ru   rl   rv   )producing_opsr"   r   rw   Zproduced_byr   r   r   _blob_to_node  s    
r   c                 C   s*   |sdS | D ]}| dr|d qdS )a  
    Removes debug information from operators, they are copious.

    Args:
        ops: List of Caffe2 operators
        perform_clear: Boolean passed from _operators_to_graph_def specifying
            whether to remove the debug information. This boolean is passed into
            this function to reduce the complexity of _operators_to_graph_def.

    Returns:
        None. Modifies the list of Caffe2 operators in-place and removes the
        'debug_info' field.

    NZ
debug_info)rb   Z
ClearField)r$   Zperform_clearr7   r   r   r   _clear_debug_info  s
    
r   c                 C   s   |  ddk p|  ddk S )a-  
    Blobs with names containing '_m' or 'grad' are part of the backward pass.
        This function references facebookresearch/Detectron/detectron/utils/net.py.

    Args:
        blob: The blob to inspect

    Returns:
        Boolean representing whether this blob is part of the forward pass
    Z__mr   Zgrad)findblobr   r   r   _check_if_forward  s    r   c                 C   s   |  d S )z
    Check if the blob's name starts with '_gpu'.

    Args:
        blob: The blob to inspect

    Returns:
        Boolean representing whether this blob is associated with a gpu
    Z_gpu
startswithr   r   r   r   _check_if_cpu'  s    
r   c                    s   t  }t  }| D ]0}|jD ]}|| q|jD ]}|| q0qt||}t||}dd |D   fdd|D }| |fS )a]  
    Find the input, intermediate and output nodes of a set of operators.

    Args:
        ops: List of Caffe2 operators to look through

    Returns:
        input_blobs: The input nodes of the set of operators
        inter_blobs: The intermediate nodes of the set of operators
        output_blobs: The output nodes of the set of operators
    c                 S   s   h | ]}| d r|qS )_r   r/   br   r   r   	<setcomp>K  s     
 z"_compute_in_out.<locals>.<setcomp>c                    s   g | ]}| kr|qS r   r   r   r{   r   r   rY   L  s      z#_compute_in_out.<locals>.<listcomp>)r3   r9   r   r:   r8   
difference)r$   Zin_blobsZ	out_blobsr7   
input_bloboutput_blobinput_blobsZoutput_blobsr   r{   r   _compute_in_out4  s    

r   c           	         s   |s| S g }| D ]z}t |j}t |j}|jdd= |jdd=  fdd|D } fdd|D }|r|j| |j| || q|S )a  
    Filter unwanted operators based on criteria in 'filter_fn'.

    Args:
        ops: List of Caffe2 operators to filter
        filter_fn: Criteria function for whether inputs/outputs in an operator
            should be filtered.
        perform_filter: Boolean passed from _operators_to_graph_def specifying
            whether to filter operators

    Returns:
        new_ops: Subset of ops containing a subset of their inputs and outputs.
    Nc                    s   g | ]} |r|qS r   r   )r/   r   	filter_fnr   r   rY   h  s      z_filter_ops.<locals>.<listcomp>c                    s   g | ]} |r|qS r   r   ry   r   r   r   rY   i  s      )r8   r9   r:   r;   r   )	r$   r   Zperform_filterZnew_opsr7   r>   r?   Z
new_inputsZnew_outputsr   r   r   _filter_opsQ  s    

r   $TFc                 C   sz  |dk	r|   ni }|t| t|| t|t|}t|t|}|rXt| ||| |rjt| ||| |rzt	| || |rt
| || t| |rt| || i }t }	t|\}
}}t }t|
}|D ]x}|rt|||n
t| |g}|j| |jD ]}|	| qt|jD ]*\}}|	| ||g ||f qq|rP|
}	t|	D ]}|jt|i |g qX|S )a1  
    Main function to convert set of operators to a graph.

    Args:
        shapes: Dictionary mapping blob names to their shapes/dimensions.
        ops: List of Caffe2 operators, representing some computation graph
        ### **kwargs (model_to_graph_def, nets_to_graph_def, protos_to_graph_def) ###
        colon_replacement: Symbol to replace ':' with. ':i' in TF has a special
            meaning, so we need to replace it with a non-conflicting symbol.
        with_ssa: Boolean
        with_gradient_scope: Boolean
        blob_name_tracker: Dictionary tracking names of blobs (inputs/outputs
            from operators)
        show_simplified: Whether to show a simplified version of the model graph
            Sets all of the following values:
                clear_debug_info: Boolean representing whether to silence debug
                    info (which can be very verbose)
                show_forward_only: Boolean representing whether to only show
                    blobs involved in the forward pass
                show_cpu_only: Boolean representing whether to only show blobs
                    that are not associated with a gpu
                use_tensorflow_naming: Boolean representing whether to convert
                    some common Caffe2 naming conventions to their Tensorflow
                    counterparts
        custom_rename: Function string -> string that defines a custom
            renaming function to use.

    Returns:
        current_graph: GraphDef representing the computation graph formed by the
            set of operators.
    N)r<   r=   rC   r   r   r   r   r    rU   r@   rP   r_   r%   r3   r   r   r   rx   noder;   r9   r   	enumerater:   
setdefaultr   sortedr   )r"   r$   Zcolon_replacementZwith_ssaZwith_gradient_scoper#   Zshow_simplifiedZcustom_renamer   Zblobsr   r|   r   Zcurrent_graphr   r7   Znodes_from_opr   r   r   r   r   r   r   _operators_to_graph_deft  sN    )




r   c                 C   s6   |  dsdS | jD ]}| ds|j| j qdS )a  
    Propagate the device options from net to operators.

    Args:
        net_def: A caffe2_pb2.NetDef representing a computation graph. The graph
            consists of Caffe2 operators.

    Returns:
        None. Iterates through all ops contained within the net. For each op,
            modifies the op device_option in-place to be the net device_option
            if the op has no pre-existing device_option, and leaves the op as-is
            if it already has a device_option.
    re   N)rb   r7   re   ZCopyFrom)net_defr7   r   r   r   _propagate_device_option  s
    


r   c              
   C   sP   zt | \}}|W S  tk
rJ } ztd| i  W Y S d}~X Y nX dS )a  
    Get missing shapes for all blobs contained in the nets.

    Args:
        nets: List of core.Net to extract blob shape information from.

    Returns:
        Dictionary containing blob name to shape/dimensions mapping. The net
            is a computation graph that is composed of operators, and the
            operators have input and output blobs, each with their own dims.
    zFailed to compute shapes: %sN)r   ZInferShapesAndTypesrc   loggingwarning)netsr"   r   er   r   r   _try_get_shapes  s    r   c                 K   s   | j | jg}t|f|S )a  
    Convert a Caffe2 model to a Tensorflow graph. This function extracts
    'param_init_net' and 'net' from the model and passes it to nets_to_graph()
    for further processing.

    Args:
        model (cnn.CNNModelHelper, model_helper.ModelHelper): The model to
            extract the nets (instances of core.Net) from.

    Returns:
        Call to nets_to_graph_def() with extracted 'param_init_net', 'net' and
            **kwargs. See _operators_to_graph_def for detailed **kwargs.
    )Zparam_init_netnetnets_to_graph_def)modelkwargsr   r   r   r   model_to_graph_def  s    r   c                 K   s*   i }dd | D } t |}t| |f|S )a  
    Convert a set of Caffe2 nets to a Tensorflow graph.

    Args:
        nets: List of core.Nets. core.Net is a wrapper around a NetDef protobuf.
            The corresponding protobuf can be extracted using .Proto().
        shapes: Dictionary mapping blob names to their shapes/dimensions.

    Returns:
        Call to protos_to_graph_def() with the extracted NetDef protobufs and
            **kwargs. See _operators_to_graph_def for detailed **kwargs.
    c                 S   s   g | ]}t | qS r   )copydeepcopyZProto)r/   r   r   r   r   rY     s     z%nets_to_graph_def.<locals>.<listcomp>)r   r   protos_to_graph_def)r   r"   r   r   r   r   r     s    
r   c                 K   s<   | D ]}t | qt|pi }dd | D }t||f|S )a  
    Convert a set of Caffe2 net definitions to a Tensorflow graph.

    Args:
        net_defs: List of caffe2_pb2.NetDef protobufs representing computation
            graphs.
        shapes: Dictionary mapping blob names to their shapes/dimensions.

    Returns:
        Call to _operators_to_graph_def() with the extracted operators from the
            NetDefs and **kwargs. See _operators_to_graph_def for detailed
            **kwargs.
    c                 S   s   g | ]}|j D ]}|qqS r   )r7   )r/   r   r7   r   r   r   rY   3  s       z'protos_to_graph_def.<locals>.<listcomp>)r   r   r   r   )Znet_defsr"   r   r   r$   r   r   r   r   "  s
    
r   )r   )r   TTNFN)N)N)2r   r   rV   r   Z"tensorboard.compat.proto.graph_pb2r   Z%tensorboard.compat.proto.node_def_pb2r   Z)tensorboard.compat.proto.tensor_shape_pb2r   builtinsr   Zcaffe2.protor   Zcaffe2.pythonr   r   typingr	   r
   r   r   r4   r5   r   r%   r@   rC   rI   r    rP   rU   r_   rf   rl   rt   rx   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   sT   (6/-=#&      
Z
