U
    dmK                     @   s   d Z ddlmZ ddlmZ dae a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d+d, Zd-d. Zd/d0 Zd6d2d3Zd7d4d5Zd1S )8z
Implement functions for controlling execution of nets and steps, including
  Do
  DoParallel
  For-loop
  While-loop
  Do-While-loop
  Switch
  If
    )core)	viewitems   c                 C   s<   d|| f }|}|t kr.d|tf }td7 aqt | |S )Nz%s/%sz%s_%dr   )_used_step_names_current_idxadd)control_name	base_nameZconcat_nameZ	next_name r
   9/tmp/pip-unpacked-wheel-ua33x9lu/caffe2/python/control.py_get_next_step_name   s    

r   c                 C   sH   t | dkrtdn.t | dkr<| d }t|tsD|g}nt| }|S )zx input is a tuple.
    Example:
    (a, b, c)   --> [a, b, c]
    (a)         --> [a]
    ([a, b, c]) --> [a, b, c]
    r   zinput cannot be empty.r   )len
ValueError
isinstancelist)inputoutputr
   r
   r   	_MakeList(   s    
r   c                 C   s,   t | trtdd | D S t | tjS d S )Nc                 s   s   | ]}t |tjV  qd S )N)r   r   Net).0nr
   r
   r   	<genexpr>=   s     z_IsNets.<locals>.<genexpr>)r   r   allr   r   )nets_or_stepsr
   r
   r   _IsNets;   s    
r   c                 G   s6   t | f} t |}t| r"||  S td|g|  S d S )Nprependr   r   Dor   netsr
   r
   r   _PrependNetsB   s
    
r    c                 G   s6   t | f} t |}t| r"| | S | td|g S d S )Nappendr   r   r
   r
   r   _AppendNetsK   s
    
r"   c                 C   s6   t |  jdks"td| jj t|  jd S )zW
    The condition blob is the last external_output that must
    be a single bool
    r   z6Condition net %s must has at least one external output)r   ProtoZexternal_outputAssertionErrornamer   BlobReference)condition_netr
   r
   r   GetConditionBlobFromNetT   s    r)   c                  G   sH   t | } td}| D ],\}}|jg |gg |tjjd}|| q|S )a  A net assigning constant bool values to blobs. It is mainly used for
    initializing condition blobs, for example, in multi-task learning, we
    need to access reader_done blobs before reader_net run. In that case,
    the reader_done blobs must be initialized.

    Args:
    blobs_with_bool_value: one or more (blob, bool_value) pairs. The net will
    assign each bool_value to the corresponding blob.

    returns
    bool_net: A net assigning constant bool values to blobs.

    Examples:
    - BoolNet((blob_1, bool_value_1), ..., (blob_n, bool_value_n))
    - BoolNet([(blob_1, net1), ..., (blob_n, bool_value_n)])
    - BoolNet((cond_1, bool_value_1))
    bool_net)shapevalueZdtype)r   r   r   ZConstantFillZDataTypeZBOOLAddExternalOutput)Zblobs_with_bool_valuer*   ZblobZ
bool_valueout_blobr
   r
   r   BoolNetb   s    
r/   c                 C   s@   t | tjrt| }n| }td}||}|| ||fS )a/  Not of a condition blob or net

    Args:
    condition_blob_or_net can be either blob or net. If condition_blob_or_net
    is Net, the condition is its last external_output
    that must be a single bool.

    returns
    not_net: the net NOT the input
    out_blob: the output blob of the not_net
    not_net)r   r   r   r)   ZNotr-   )condition_blob_or_netcondition_blobr0   r.   r
   r
   r   NotNet   s    



r3   c                 C   s&   t d}|| }|| ||fS )zMake a condition net that copies the condition_blob

    Args:
    condition_blob is a single bool.

    returns
    not_net: the net NOT the input
    out_blob: the output blob of the not_net
    Zcopy_condition_blob_net)r   r   ZCopyr-   )r2   r(   r.   r
   r
   r   _CopyConditionBlobNet   s    



r4   c           
      C   s  t |ts|S t|dkr*|r&|d S dS t| }tt|D ]}||  }|j| jksdt|j	| j	ksxt| j
|j
 | j|j t|| }|dkr|}n||||g}t|| jD ]\}}	|j|  |	7  < qq@|| |S )a  
    Merge multi condition nets into a single condition nets.

    Args:
        name: name of the new condition net.
        condition_nets: a list of condition nets. The last external_output
                        of each condition net must be single bool value.
        relation: can be 'And' or 'Or'.

    Returns:
        - A new condition net. Its last external output is relation of all
          condition_nets.
    r   r   N)r   r   r   r   r   ranger$   Zdevice_optionr%   typeopextendZexternal_inputr)   __getattr__r   Z
_attr_dictr-   )
r&   condition_netsrelationZ
merged_netiZ	net_proto	curr_cond	last_condkvr
   r
   r   MergeConditionNets   s&    


rA   c           
      C   s   |sdS t |tstdt|dkrBt|d }t|\}}|S t| }tt|D ]0}t|| }|dkrv|}	qX|	||	|g}	qX|
|	 |S )a  
    Combine conditions of multi nets into a single condition nets. Unlike
    MergeConditionNets, the actual body of condition_nets is not copied into
    the combine condition net.

    One example is about multi readers. Each reader net has a reader_done
    condition. When we want to check whether all readers are done, we can
    use this function to build a new net.

    Args:
        name: name of the new condition net.
        condition_nets: a list of condition nets. The last external_output
                        of each condition net must be single bool value.
        relation: can be 'And' or 'Or'.

    Returns:
        - A new condition net. Its last external output is relation of all
          condition_nets.
    Nz&condition_nets must be a list of nets.r   r   )r   r   r   r   r)   r4   r   r   r5   r9   r-   )
r&   r:   r;   r2   r(   _Zcombined_netr<   r=   r>   r
   r
   r   CombineConditions   s$    


rC   c                 G   sB   t |}t|dkr,t|d tjr,|d S ttd| |S dS )z
    Execute the sequence of nets or steps once.

    Examples:
    - Do('myDo', net1, net2, ..., net_n)
    - Do('myDo', list_of_nets)
    - Do('myDo', step1, step2, ..., step_n)
    - Do('myDo', list_of_steps)
    r   r   r   Nr   r   r   r   ZExecutionStepscoped_execution_stepr   r&   r   r
   r
   r   r      s    
  r   c                 G   sF   t |}t|dkr,t|d tjr,|d S tjtd| |ddS dS )a  
    Execute the nets or steps in parallel, waiting for all of them to finish

    Examples:
    - DoParallel('pDo', net1, net2, ..., net_n)
    - DoParallel('pDo', list_of_nets)
    - DoParallel('pDo', step1, step2, ..., step_n)
    - DoParallel('pDo', list_of_steps)
    r   r   
DoParallelT)Zconcurrent_substepsNrD   rF   r
   r
   r   rG     s    
 rG   c                    st   t |\}t|tjr&t||n
t| fdd}trhtdf}t d ||dS |dS dS )a  
    Execute nets_or_steps once if condition_blob_or_net evaluates as true.

    If condition_blob_or_net is Net, the condition is its last external_output
    that must be a single bool. And this net will be executed before
    nets_or_steps so as to get the condition.
    c                    s   t jt|  ddS )NTshould_stop_blobZ	only_oncer   rE   r   r   r&   r   	stop_blobr
   r   if_step7  s    z_RunOnceIf.<locals>.if_stepFz/_RunOnceIfz_RunOnceIf-inner
_RunOnceIfNr3   r   r   r   r    r   r/   r   )r&   r1   r   condition_not_netrN   r*   r
   rL   r   rO   (  s       
 rO   c                 C   sN   t |tjr t|}t||}nt|\}}t||}tjtd| ||ddS )zq
    Similar to _RunOnceIf() but Execute nets_or_steps once if
    condition_blob_or_net evaluates as false.
    _RunOnceIfNotTrH   )r   r   r   r)   r    r4   rE   r   )r&   r1   r   r2   Zcopy_netr
   r
   r   rR   G  s    
rR   c                 C   sd   t d}|jg |d}t d}||g}t jtd| t|||d}t| d t| d ||S )a  
    Execute nets_or_steps iter_num times.

    Args:
    nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
                   a list nets.
    iter_num:    the number times to execute the nets_or_steps.

    Returns:
    A ExecutionStep instance.
    zinit-net)Z
init_countzFor-iterz	For-innerrI   z/Forz/For-init-net)r   r   ZCreateCounterZ	CountDownrE   r   r    r   )r&   r   Ziter_numZinit_netZiter_cntZiter_netZ	iter_doneZfor_stepr
   r
   r   For[  s    

rT   c                    st   t |\}t|tjr&t||n
t| fdd}trhtdf}t d ||dS |dS dS )aX  
    Execute nets_or_steps when condition_blob_or_net returns true.

    Args:
    condition_blob_or_net: If it is an instance of Net, its last
      external_output must be a single bool.
    nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
                   a list nets.

    Returns:
    A ExecutionStep instance.
    c                    s   t jt|  dS )NrS   rJ   rK   rL   r
   r   
while_step  s
    zWhile.<locals>.while_stepFz/WhilezWhile-innerWhileNrP   )r&   r1   r   rQ   rU   r*   r
   rL   r   rV   u  s      
rV   c                 C   sD   t |tjr t|}t||}ntt|}tjtd| ||dS )zc
    Similar to While() but execute nets_or_steps when
    condition_blob_or_net returns false
    UntilrS   )	r   r   r   r)   r    r'   strrE   r   )r&   r1   r   rM   r
   r
   r   rW     s    rW   c                 C   s^   t |\}}t|tjr&t|||}n
t||}t|df}t| d |tjtd| ||dS )a  
    Execute nets_or_steps when condition_blob_or_net returns true. It will
    execute nets_or_steps before evaluating condition_blob_or_net.

    Args:
    condition_blob_or_net: if it is an instance of Net, tts last external_output
      must be a single bool.
    nets_or_steps: a ExecutionStep or a Net or a list of ExecutionSteps or
                   a list nets.

    Returns:
    A ExecutionStep instance.
    Fz/DoWhilezDoWhile-innerrS   )	r3   r   r   r   r"   r/   r   rE   r   )r&   r1   r   rQ   rM   r*   r
   r
   r   DoWhile  s      
rY   c                 C   sl   t |tjs,t|}tjtd| ||dS t||}t|}t|df}t	| d |tjtd| ||dS )a  
    Similar to DoWhile() but execute nets_or_steps when
    condition_blob_or_net returns false. It will execute
    nets_or_steps before evaluating condition_blob_or_net.

    Special case: if condition_blob_or_net is a blob and is pre-set to
    true, then only the first net/step of nets_or_steps will be executed and
    loop is exited. So you need to be careful about the initial value the
    condition blob when using DoUntil(), esp when DoUntil() is called twice.
    DoUntilrS   Fz/DoUntilzDoUntil-inner)
r   r   r   r'   rE   r   r"   r)   r/   r   )r&   r1   r   rM   r*   r
   r
   r   rZ     s    

rZ   c                    s(   t |}ttd  fdd|D S )a  
    Execute the steps for which the condition is true.
    Each condition is a tuple (condition_blob_or_net, nets_or_steps).
    Note:
      1. Multi steps can be executed if their conditions are true.
      2. The conditions_blob_or_net (if it is Net) of all steps will be
         executed once.

    Examples:
    - Switch('name', (cond_1, net_1), (cond_2, net_2), ..., (cond_n, net_n))
    - Switch('name', [(cond_1, net1), (cond_2, net_2), ..., (cond_n, net_n)])
    - Switch('name', (cond_1, net_1))
    Switchc                    s    g | ]\}}t  d  ||qS )z/Switch)rO   r   Zcondstepr&   r
   r   
<listcomp>  s     zSwitch.<locals>.<listcomp>r   r   rE   r   r&   Z
conditionsr
   r^   r   r[     s
    r[   c                    s(   t |}ttd  fdd|D S )zU
    Similar to Switch() but execute the steps for which the condition is False.
    	SwitchNotc                    s    g | ]\}}t  d  ||qS )z
/SwitchNot)rR   r\   r^   r
   r   r_     s   zSwitchNot.<locals>.<listcomp>r`   ra   r
   r^   r   rb     s    
rb   Nc                 C   sV   |st | d ||S t|tjr*t|}n|}t| d t | d ||t| d ||S )a  
    condition_blob_or_net is first evaluated or executed. If the condition is
    true, true_nets_or_steps is then executed, otherwise, false_nets_or_steps
    is executed.

    If condition_blob_or_net is Net, the condition is its last external_output
    that must be a single bool. And this Net will be executred before both
    true/false_nets_or_steps so as to get the condition.
    z/Ifz/If-truez	/If-false)rO   r   r   r   r)   r   rR   r&   r1   Ztrue_nets_or_stepsZfalse_nets_or_stepsr2   r
   r
   r   If  s      
 rd   c                 C   sV   |st | d ||S t|tjr*t|}n|}t| d t | d ||t| d ||S )zy
    If condition_blob_or_net returns false, executes true_nets_or_steps,
    otherwise executes false_nets_or_steps
    z/IfNotz/IfNot-truez/IfNot-false)rR   r   r   r   r)   r   rO   rc   r
   r
   r   IfNot+  s      
 re   )N)N) __doc__Zcaffe2.pythonr   Zfuture.utilsr   r   setr   r   r   r   r    r"   r)   r/   r3   r4   rA   rC   r   rG   rO   rR   rT   rV   rW   rY   rZ   r[   rb   rd   re   r
   r
   r
   r   <module>   s<   		 ),(!! 
 