ó
àÆ÷Xc           @` sâ   d  Z  d d l m Z m Z m Z d d l m Z m Z d d l m	 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 e f d „  ƒ  YZ d „  Z d „  Z d „  Z d e j f d „  ƒ  YZ d S(   sX   
Classes and functions for validating graphs that contain view
and inplace operations.

i    (   t   absolute_importt   print_functiont   division(   t   dequet   OrderedDict(   t	   iteritemsNi   (   t   toolbox(   t   graph(   t
   OrderedSet(   t   InconsistencyErrort   ProtocolErrorc           B` s   e  Z d  Z RS(   s¤   
    Raised when FunctionGraph calls DestroyHandler callbacks in
    an invalid way, for example, pruning or changing a node that has
    never been imported.

    (   t   __name__t
   __module__t   __doc__(    (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyR
      s   c         C` s²  |  j  } t | t t t f ƒ s' t ‚ i  } i  } t ƒ  } x] |  j D]R } | j } | r | j | g  ƒ j	 | ƒ d | | <qF | j	 | ƒ d | | <qF Wx“ |  j
 D]ˆ } t | j ƒ }	 |	 j | j | g  ƒ ƒ |	 rx' |	 D] }
 | j |
 g  ƒ j	 | ƒ qá Wt |	 ƒ | | <q¦ | j	 | ƒ d | | <q¦ Wd } xg | r¡| j ƒ  } | d 7} xD | j | g  ƒ D]0 } | | c d 8<| | sj| j	 | ƒ qjqjWq;W| t | ƒ k S(   sù  
    Function to check if the given graph contains a cycle

    Parameters
    ----------
    fgraph
        The FunctionGraph to check for cycles.
    orderings
        Dictionary specifying extra dependencies besides those encoded in
        Variable.owner / Apply.inputs.

        If orderings[my_apply] == dependencies, then my_apply is an Apply
        instance, dependencies is a set of Apply instances, and every member
        of dependencies must be executed before my_apply.

        The dependencies are typically used to prevent
        inplace apply nodes from destroying their input before
        other apply nodes with the same input access it.

    Returns
    -------
    bool
        True if the graph contains a cycle, False otherwise.

    i   i    (   t   outputst
   isinstancet   tuplet   listR   t   AssertionErrort	   variablest   ownert
   setdefaultt   appendt   apply_nodest   inputst   extendt   gett   lent   popleft(   t   fgrapht	   orderingsR   t   parent_countst   node_to_childrent	   visitablet   varR   t   a_nt   parentst   parentt   visitedt   nodet   client(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   _contains_cycle   s<    				

c         C` sÁ  i  } i  } i  } xŸ|  j  D]”} x‹| j j j ƒ  D]w\ } } t | ƒ d k r_ t ƒ  ‚ n  | d } | j | } |  j }	 | }
 x% |
 d  k	 r¬ |
 } |	 j	 | ƒ }
 qˆ W| } | | k rÒ t
 d | ƒ ‚ n  | | | <| | | <t ƒ  } t ƒ  } | j | ƒ x\ t | ƒ d k rc| j ƒ  } x7 |  j j	 | g  ƒ D]  } | j | ƒ | j | ƒ q<WqWx* | D]" } | | k sƒt ‚ | | | <qkW| | | <| | j | ƒ q5 Wq W| | | f S(   Ni   i    s   Multiple destroyers of %s(   t
   destroyerst   opt   destroy_mapt   itemsR   t   NotImplementedErrorR   t   view_it   NoneR   R	   R   R   R   R   t   view_ot   addR   (   t   destroy_handlert   droott   impactt   root_destroyert   appt
   output_idxt   input_idx_listt	   input_idxt   inputR/   t   _rt   rt
   input_roott   input_impactt   qt   vt   n(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   _build_droot_impact·   sD    
	

		
c         C` sµ   |  d j  } t j j j } g  | j D] } t | | ƒ r& | j ^ q& } t | g  ƒ } | j	 | j
 ƒ g  |  D]; } t | t j ƒ rp | j | ƒ rp | | k rp | ^ qp }  |  S(   sâ   
    Return the variables in inputs that are posible candidate for as inputs of
    inplace operation.

    Parameters
    ----------
    inputs : list
        Inputs Variable that you want to use as inplace destination.

    i    (   R   t   theanot   compilet   function_modulet
   Supervisort	   _featuresR   t	   protectedt   sumR   R   R   t   ConstantR*   (   R   R   RG   t   ft   protected_inputst   i(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   fast_inplace_checkç   s    t   DestroyHandlerc           B` st   e  Z d  Z d g Z e d „ Z d „  Z d „  Z d „  Z d „  Z	 d „  Z
 d „  Z d	 „  Z d
 „  Z d „  Z RS(   sú  
    The DestroyHandler class detects when a graph is impossible to evaluate
    because of aliasing and destructive operations.

    Several data structures are used to do this.

    An Op can use its view_map property to declare that an output may be
    aliased to an input. If that output is destroyed, the input is also
    considered to be destroyed. The view_maps of several Ops can feed into
    one another and form a directed graph. The consequence of destroying any
    variable in such a graph is that all variables in the graph must be
    considered to be destroyed, because they could all be refering to the
    same underlying storage.

    In the current implementation, that graph is a tree, and the root of that
    tree is called the foundation.

    TODO: why "in the current implementation" ? is there another implementation
          planned?
    TODO: why is the graph a tree? isn't it possible that one variable could
          be aliased to many variables? for example, don't switch and ifelse
          have to do this?

    The original DestroyHandler (if 0'ed out above) computed several data
    structures from scratch each time it was asked to validate the graph.
    Because this happens potentially thousands of times and each graph to
    validate is extremely similar to the previous one, computing the
    data structures from scratch repeatedly was wasteful and resulted in
    high compile times for large graphs.

    This implementation computes the data structures once at initialization
    and then incrementally updates them.

    It is a work in progress. The following data structures have been
    converted to use the incremental strategy:
        <none>

    The following data structures remain to be converted:
        <unknown>

    R*   c         C` s:   d  |  _ | |  _ t ƒ  |  _ t ƒ  |  _ t ƒ  |  _ d  S(   N(   R0   R   t   do_imports_on_attachR   R4   R5   R6   (   t   selfRQ   (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   __init__˜  s
    		c         C` s  t  } |  j | k r t } n  |  j d k	 r< t d ƒ ‚ n  x& d D] } t | | ƒ rC t } qC qC W| r} t j d ƒ ‚ n  |  j | ƒ |  | _	 | |  _ t
 ƒ  |  _ t ƒ  |  _ t ƒ  |  _ t ƒ  |  _ t |  _ t
 ƒ  |  _ |  j r t j j |  | ƒ n  d S(   s~  
        When attaching to a new fgraph, check that
            1) This DestroyHandler wasn't already attached to some fgraph
               (its data structures are only set up to serve one).
            2) The FunctionGraph doesn't already have a DestroyHandler.
               This would result in it validating everything twice, causing
               compilation to be slower.

        Give the FunctionGraph instance:
            1) A new method "destroyers(var)"
               TODO: what does this do exactly?
            2) A new attribute, "destroy_handler"
        TODO: WRITEME: what does this do besides the checks?

        sJ   A DestroyHandler instance can only serve one FunctionGraph. (Matthew 6:24)R*   R3   sM   DestroyHandler feature is already present or in conflict with another plugin.N(   s
   destroyerss   destroy_handler(   t   FalseR   t   TrueR0   t	   Exceptiont   hasattrR   t   AlreadyTheret   unpickleR3   R   R*   R   R/   R1   t   clientst   stale_droott   debug_all_appsRQ   t
   Bookkeepert	   on_attach(   RR   R   t   already_theret   attr(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyR^   ·  s.    					c         ` s   ‡  f d †  } | | _  d  S(   Nc         ` sA   ˆ  j  ƒ  \ } } } y | | |  g SWn t k
 r< g  SXd  S(   N(   t   refresh_droot_impactRV   (   R=   R4   R5   R6   (   RR   (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   get_destroyers_ofë  s
    (   R*   (   RR   R   Rb   (    (   RR   s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyRY   ê  s    c         C` sI   |  j  r3 t |  ƒ \ |  _ |  _ |  _ t |  _  n  |  j |  j |  j f S(   s¤   
        Makes sure self.droot, self.impact, and self.root_destroyer are up to
        date, and returns them (see docstrings for these properties above).

        (   R[   RC   R4   R5   R6   RT   (   RR   (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyRa   ó  s    	c         C` s„   | |  j  k	 r! t d | ƒ ‚ n  |  ` |  ` |  ` |  ` |  ` |  j  j |  k sW t ‚ t	 |  j  d ƒ t	 |  j  d ƒ d  |  _  d  S(   Ns   detaching wrong fgraphR*   R3   (   R   RV   R*   R/   R1   RZ   R[   t   destroyer_handlerR   t   delattrR0   (   RR   R   (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt	   on_detachÿ  s    c   
      C` s  | |  j  k r t d ƒ ‚ n  |  j  j | ƒ t | j d i  ƒ rV |  j j | ƒ n  xš t t | j d i  ƒ ƒ D]} \ } } t | ƒ d k r¥ t d | j ƒ ‚ n  | j	 | } | j
 | d } | |  j | <|  j j | t ƒ  ƒ j | ƒ qr WxV t | j
 ƒ D]E \ } } |  j j | t ƒ  ƒ j | d ƒ |  j | | c d 7<qWx3 t | j	 ƒ D]" \ } }	 |  j j |	 t ƒ  ƒ q\Wt |  _ d S(   sD   
        Add Apply instance to set which must be computed.

        s   double importR,   t   view_mapi   s2   destroying this output invalidates multiple inputsi    N(   R\   R
   R2   t   getattrR+   R*   R   R   R.   R   R   R/   R1   R   R   t	   enumerateRZ   R   RU   R[   (
   RR   R   R7   t   reasont   o_idxt
   i_idx_listt   oRN   R;   t   output(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt	   on_import  s(    (#"c   	      C` s=  | |  j  k r t d ƒ ‚ n  |  j  j | ƒ x1 t t | j ƒ ƒ D] \ } } |  j | | =qD Wt | j d t	 ƒ  ƒ r |  j
 j | ƒ n  x  t t | j d t	 ƒ  ƒ ƒ D]€ \ } } t | ƒ d k rÖ t ƒ  ‚ n  | j | } | j | d } |  j | =|  j | j | ƒ |  j | s¬ |  j | =q¬ q¬ Wt |  _ d S(   sI   
        Remove Apply instance from set which must be computed.

        s   prune without importR,   Rf   i   i    N(   R\   R
   t   removeRh   R   R   RZ   Rg   R+   R   R*   R   R   R.   R   R/   R1   RU   R[   (	   RR   R   R7   Ri   RN   R;   Rj   Rk   Rl   (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   on_prune0  s$    "
c         C` s¡  | d k r n…| |  j  k r- t d ƒ ‚ n  |  j | | c d 8<|  j | | d k rl |  j | | =n  |  j j | t ƒ  ƒ j | d ƒ |  j | | c d 7<xì t t | j d t ƒ  ƒ ƒ D]Ì \ } } t | ƒ d k rî t	 ƒ  ‚ n  | d }	 | j
 | }
 |	 | k rÄ | j |	 | k	 r3t d ƒ ‚ n  | |  j |
 <|  j | j |
 ƒ |  j | sn|  j | =n  |  j j | t ƒ  ƒ j |
 ƒ qÄ qÄ Wt |  _ d S(   s=   
        app.inputs[i] changed from old_r to new_r.

        Rm   s   change without importi   i    Rf   s   wrong new_r on changeN(   R\   R
   RZ   R   R   R   Rg   R+   R   R.   R   R   R/   R1   Ro   R   R2   RU   R[   (   RR   R   R7   RN   t   old_rt   new_rRi   Rj   Rk   t   i_idxRm   (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   on_change_inputU  s0    "
&c         C` s=   |  j  r9 |  j | ƒ } t | | ƒ r9 t d ƒ ‚ q9 n  t S(   s¢   
        Return None.

        Raise InconsistencyError when
        a) orderings() raises an error
        b) orderings cannot be topologically sorted.

        s    Dependency graph contains cycles(   R*   R   R)   R	   RU   (   RR   R   t   ords(    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   validate€  s
    		c         ` s   t  ƒ  } |  j rœ|  j ƒ  \ } } } g  | D]3 } t | j d t ƒ s[ t | t j ƒ r. | ^ q. } | r€ t	 d | ƒ ‚ n  x|  j D]} t
 ƒ  }	 xÙt | j j ƒ D]Å\ }
 } | d ‰  | j ˆ  } | | } | | } t | j d g  ƒ } t | t ƒ st ‚ t
 ‡  f d †  | Dƒ ƒ } | j ˆ  ƒ t | j d g  ƒ } t | t ƒ s`t ‚ t
 ‡  f d †  | Dƒ ƒ } xo t | j ƒ D]^ \ } } | | k rªqŒn  | | k rŒ| | k sÎ| | k	 rŒt	 d | ˆ  | f ƒ ‚ qŒqŒWx€ | D]x } g  |  j | j ƒ  D] \ } } | s| ^ qs4t ‚ |	 j g  |  j | j ƒ  D] \ } } | rN| ^ qNƒ qõWq¬ W|	 j | ƒ |	 rŠ |	 | | <qŠ qŠ Wn  | S(	   s1  
        Return orderings induced by destructive operations.

        Raise InconsistencyError when
        a) attempting to destroy indestructable variable, or
        b) attempting to destroy a value multiple times, or
        c) an Apply destroys (illegally) one of its own inputs by aliasing

        t   indestructibles2   Attempting to destroy indestructible variables: %si    t   destroyhandler_tolerate_samec         3` s'   |  ] \ } } | ˆ  k r | Vq d  S(   N(    (   t   .0t   idx0t   idx1(   t   destroyed_idx(    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pys	   <genexpr>ç  s    	t   destroyhandler_tolerate_aliasedc         3` s'   |  ] \ } } | ˆ  k r | Vq d  S(   N(    (   Ry   Rz   R{   (   R|   (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pys	   <genexpr>í  s    	s   Input aliasing: %s (%i, %i)(   R   R*   Ra   Rg   t   tagRT   R   R   RK   R	   R   R   R+   R,   R   R   R   R2   Rh   RZ   R-   t   updateRo   (   RR   R   t   rvalR4   R5   t   _DestroyHandler__ignoreR=   t   illegal_destroyR7   t   root_clientsR8   R9   t   destroyed_variablet   roott   root_impactt   tolerate_samet	   toleratedt   tolerate_aliasedt   ignoredRN   R;   t   at   c(    (   R|   s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyR   œ  sR    
			


$	9A(   R   R   R   t   pickle_rm_attrRU   RS   R^   RY   Ra   Re   Rn   Rp   Rt   Rv   R   (    (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyRP   l  s   )		3					$	%	+	(   R   t
   __future__R    R   R   t   collectionsR   R   t   sixR   RD   t    R   R   t   theano.misc.ordered_setR   t   fgR	   RV   R
   R)   RC   RO   R]   RP   (    (    (    s9   /tmp/pip-build-X4mzal/theano/theano/gof/destroyhandler.pyt   <module>   s   	˜	0	ÿ †