B
    ÛT•\µ1  ã               @   sÎ   d dl mZmZmZ d dlmZ d dlmZmZ dd„ Z	dd„ Z
G dd	„ d	eƒZG d
d„ deƒZedƒZedƒZG dd„ deƒZG dd„ deƒZG dd„ deƒZdd„ Zdd„ ZeedœZdd„ Zdd„ ZdS )é    )Úabsolute_importÚdivisionÚprint_function)Údeque)ÚistaskÚsubsc             C   s&   t | ƒr| d S t| tƒrtS | S dS )z#Return the top level node of a taskr   N)r   Ú
isinstanceÚlist)Útask© r   ú+lib/python3.7/site-packages/dask/rewrite.pyÚhead   s
    
r   c             C   s*   t | ƒr| dd… S t| tƒr"| S dS dS )z&Get the arguments for the current taské   Nr   )r   r   r	   )r
   r   r   r   Úargs   s
    
r   c               @   sF   e Zd ZdZddd„Zdd„ Zdd„ Zd	d
„ Zedd„ ƒZ	dd„ Z
dS )Ú	Traversera£  Traverser interface for tasks.

    Class for storing the state while performing a preorder-traversal of a
    task.

    Parameters
    ----------
    term : task
        The task to be traversed

    Attributes
    ----------
    term
        The current element in the traversal
    current
        The head of the current element in the traversal. This is simply `head`
        applied to the attribute `term`.
    Nc             C   s"   || _ |sttgƒ| _n|| _d S )N)Útermr   ÚENDÚ_stack)Úselfr   Ústackr   r   r   Ú__init__2   s    zTraverser.__init__c             c   s$   x| j tk	r| j V  |  ¡  qW d S )N)Úcurrentr   Únext)r   r   r   r   Ú__iter__9   s    zTraverser.__iter__c             C   s   t | jt| jƒƒS )z…Copy the traverser in its current state.

        This allows the traversal to be pushed onto a stack, for easy
        backtracking.)r   r   r   r   )r   r   r   r   Úcopy>   s    zTraverser.copyc             C   sB   t | jƒ}|s| j ¡ | _n"|d | _| j t|dd… ƒ¡ dS )z3Proceed to the next term in the preorder traversal.r   r   N)r   r   r   ÚpopÚextendÚreversed)r   Zsubtermsr   r   r   r   F   s
    

zTraverser.nextc             C   s
   t | jƒS )N)r   r   )r   r   r   r   r   Q   s    zTraverser.currentc             C   s   | j  ¡ | _dS )z<Skip over all subterms of the current level in the traversalN)r   r   r   )r   r   r   r   ÚskipU   s    zTraverser.skip)N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r   Úpropertyr   r   r   r   r   r   r      s   
r   c               @   s    e Zd ZdZdd„ Zdd„ ZdS )ÚTokenz[A token object.

    Used to express certain objects in the traversal of a task or pattern.c             C   s
   || _ d S )N)Úname)r   r%   r   r   r   r   _   s    zToken.__init__c             C   s   | j S )N)r%   )r   r   r   r   Ú__repr__b   s    zToken.__repr__N)r   r    r!   r"   r   r&   r   r   r   r   r$   Z   s   r$   ú?Úendc               @   s6   e Zd ZdZdZd
dd„Zedd„ ƒZedd	„ ƒZdS )ÚNodezA Discrimination Net node.r   Nc             C   s(   |r|ni }|r|ng }t  | ||f¡S )N)ÚtupleÚ__new__)ÚclsÚedgesÚpatternsr   r   r   r+   r   s    zNode.__new__c             C   s   | d S )z@A dictionary, where the keys are edges, and the values are nodesr   r   )r   r   r   r   r-   w   s    z
Node.edgesc             C   s   | d S )z8A list of all patterns that currently match at this noder   r   )r   r   r   r   r.   |   s    zNode.patterns)NN)	r   r    r!   r"   Ú	__slots__r+   r#   r-   r.   r   r   r   r   r)   m   s
   
r)   c               @   s2   e Zd ZdZddd„Zdd„ Zdd„ Zd	d
„ ZdS )ÚRewriteRuleaå  A rewrite rule.

    Expresses `lhs` -> `rhs`, for variables `vars`.

    Parameters
    ----------
    lhs : task
        The left-hand-side of the rewrite rule.
    rhs : task or function
        The right-hand-side of the rewrite rule. If it's a task, variables in
        `rhs` will be replaced by terms in the subject that match the variables
        in `lhs`. If it's a function, the function will be called with a dict
        of such matches.
    vars: tuple, optional
        Tuple of variables found in the lhs. Variables can be represented as
        any hashable object; a good convention is to use strings. If there are
        no variables, this can be omitted.

    Examples
    --------
    Here's a `RewriteRule` to replace all nested calls to `list`, so that
    `(list, (list, 'x'))` is replaced with `(list, 'x')`, where `'x'` is a
    variable.

    >>> lhs = (list, (list, 'x'))
    >>> rhs = (list, 'x')
    >>> variables = ('x',)
    >>> rule = RewriteRule(lhs, rhs, variables)

    Here's a more complicated rule that uses a callable right-hand-side. A
    callable `rhs` takes in a dictionary mapping variables to their matching
    values. This rule replaces all occurrences of `(list, 'x')` with `'x'` if
    `'x'` is a list itself.

    >>> lhs = (list, 'x')
    >>> def repl_list(sd):
    ...     x = sd['x']
    ...     if isinstance(x, list):
    ...         return x
    ...     else:
    ...         return (list, x)
    >>> rule = RewriteRule(lhs, repl_list, variables)
    r   c                sf   t ˆ tƒstdƒ‚|| _t|ƒr(|| _n| j| _|| _‡ fdd„t|ƒD ƒ| _	tt
t| j	ƒƒƒ| _d S )Nz!vars must be a tuple of variablesc                s   g | ]}|ˆ kr|‘qS r   r   )Ú.0Út)Úvarsr   r   ú
<listcomp>¸   s    z(RewriteRule.__init__.<locals>.<listcomp>)r   r*   Ú	TypeErrorÚlhsÚcallabler   Ú_applyÚrhsr   Ú_varlistÚsortedÚsetr3   )r   r6   r9   r3   r   )r3   r   r   ¯   s    
zRewriteRule.__init__c             C   s,   | j }x | ¡ D ]\}}t|||ƒ}qW |S )N)r9   Úitemsr   )r   Zsub_dictr   ÚkeyÚvalr   r   r   r8   ¼   s    zRewriteRule._applyc             C   s   d  | j| j| j¡S )NzRewriteRule({0}, {1}, {2}))Úformatr6   r9   r3   )r   r   r   r   Ú__str__Â   s    zRewriteRule.__str__c             C   s   t | ƒS )N)Ústr)r   r   r   r   r&   Æ   s    zRewriteRule.__repr__N)r   )r   r    r!   r"   r   r8   rA   r&   r   r   r   r   r0   ‚   s
   +
r0   c               @   s:   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zddd„ZdS )ÚRuleSeta?  A set of rewrite rules.

    Forms a structure for fast rewriting over a set of rewrite rules. This
    allows for syntactic matching of terms to patterns for many patterns at
    the same time.

    Examples
    --------

    >>> def f(*args): pass
    >>> def g(*args): pass
    >>> def h(*args): pass
    >>> from operator import add

    >>> rs = RuleSet(                 # Make RuleSet with two Rules
    ...         RewriteRule((add, 'x', 0), 'x', ('x',)),
    ...         RewriteRule((f, (g, 'x'), 'y'),
    ...                     (h, 'x', 'y'),
    ...                     ('x', 'y')))

    >>> rs.rewrite((add, 2, 0))       # Apply ruleset to single task
    2

    >>> rs.rewrite((f, (g, 'a', 3)))  # doctest: +SKIP
    (h, 'a', 3)

    >>> dsk = {'a': (add, 2, 0),      # Apply ruleset to full dask graph
    ...        'b': (f, (g, 'a', 3))}

    >>> from toolz import valmap
    >>> valmap(rs.rewrite, dsk)  # doctest: +SKIP
    {'a': 2,
     'b': (h, 'a', 3)}

    Attributes
    ----------
    rules : list
        A list of `RewriteRule`s included in the `RuleSet`.
    c             G   s*   t ƒ | _g | _x|D ]}|  |¡ qW dS )z—Create a `RuleSet` for a number of rules

        Parameters
        ----------
        rules
            One or more instances of RewriteRule
        N)r)   Ú_netÚrulesÚadd)r   rE   Úpr   r   r   r   ó   s    
zRuleSet.__init__c             C   sš   t |tƒstdƒ‚|j}| j}t| jƒ}xNt|jƒD ]@}|}||krHt	}||j
kr^|j
| }q4tƒ |j
|< |j
| }q4W |j
| j |¡ | j |¡ dS )zeAdd a rule to the RuleSet.

        Parameters
        ----------
        rule : RewriteRule
        z$rule must be instance of RewriteRuleN)r   r0   r5   r3   rD   ÚlenrE   r   r6   ÚVARr-   r)   r.   Úappend)r   Úruler3   Z	curr_nodeZindr2   Z	prev_noder   r   r   rF      s    


zRuleSet.addc             c   sZ   t |ƒ}xLt|| jƒD ]<\}}x2|D ]*}| j| }t||ƒ}|dk	r$||fV  q$W qW dS )al  A generator that lazily finds matchings for term from the RuleSet.

        Parameters
        ----------
        term : task

        Yields
        ------
        Tuples of `(rule, subs)`, where `rule` is the rewrite rule being
        matched, and `subs` is a dictionary mapping the variables in the lhs
        of the rule to their matching values in the term.N)r   Ú_matchrD   rE   Ú_process_match)r   r   ÚSÚmÚsymsÚirK   r   r   r   r   Úiter_matches  s    


zRuleSet.iter_matchesc             C   s(   x"|   |¡D ]\}}| |¡}P qW |S )z7Apply the rewrite rules in RuleSet to top level of term)rR   r   )r   r   rK   Zsdr   r   r   Ú_rewrite0  s    
zRuleSet._rewriteÚ	bottom_upc             C   s   t | | |ƒS )ae  Apply the `RuleSet` to `task`.

        This applies the most specific matching rule in the RuleSet to the
        task, using the provided strategy.

        Parameters
        ----------
        term: a task
            The task to be rewritten
        strategy: str, optional
            The rewriting strategy to use. Options are "bottom_up" (default),
            or "top_level".

        Examples
        --------
        Suppose there was a function `add` that returned the sum of 2 numbers,
        and another function `double` that returned twice its input:

        >>> add = lambda x, y: x + y
        >>> double = lambda x: 2*x

        Now suppose `double` was *significantly* faster than `add`, so
        you'd like to replace all expressions `(add, x, x)` with `(double,
        x)`, where `x` is a variable. This can be expressed as a rewrite rule:

        >>> rule = RewriteRule((add, 'x', 'x'), (double, 'x'), ('x',))
        >>> rs = RuleSet(rule)

        This can then be applied to terms to perform the rewriting:

        >>> term = (add, (add, 2, 2), (add, 2, 2))
        >>> rs.rewrite(term)  # doctest: +SKIP
        (double, (double, 2))

        If we only wanted to apply this to the top level of the term, the
        `strategy` kwarg can be set to "top_level".

        >>> rs.rewrite(term)  # doctest: +SKIP
        (double, (add, 2, 2))
        )Ú
strategies)r   r
   Zstrategyr   r   r   Úrewrite;  s    )zRuleSet.rewriteN)rT   )	r   r    r!   r"   r   rF   rR   rS   rV   r   r   r   r   rC   Ê   s   'rC   c             C   s
   |   |¡S )N)rS   )Únetr   r   r   r   Ú
_top_levelg  s    rX   c                sX   t |ƒr.t|ƒft‡ fdd„t|ƒD ƒƒ }n t|tƒrN‡ fdd„t|ƒD ƒ}ˆ  |¡S )Nc             3   s   | ]}t ˆ |ƒV  qd S )N)Ú
_bottom_up)r1   r2   )rW   r   r   ú	<genexpr>m  s    z_bottom_up.<locals>.<genexpr>c                s   g | ]}t ˆ |ƒ‘qS r   )rY   )r1   r2   )rW   r   r   r4   o  s    z_bottom_up.<locals>.<listcomp>)r   r   r*   r   r   r	   rS   )rW   r   r   )rW   r   rY   k  s
    &
rY   )Z	top_levelrT   c             c   sÞ   t ƒ }d}d}xÊ| jtkr&|j|fV  y>|j | jd¡}|rb|sb| |  ¡ ||f¡ |}|  ¡  wW n t	k
rx   Y nX |j t
d¡}|rªd}|| jf }|  ¡  |}qy| ¡ \} }}d}W q tk
rÔ   dS X qW dS )z;Structural matching of term S to discrimination net node N.Fr   NT)r   r   r   r.   r-   ÚgetrJ   r   r   r5   rI   r   r   r   Ú	Exception)rN   ÚNr   Zrestore_state_flagZmatchesÚnr   r   r   rL   w  s6    
rL   c             C   s^   i }| j }t|ƒt|ƒks"tdƒ‚x6t||ƒD ](\}}||krN|| |krNdS |||< q.W |S )aÉ  Process a match to determine if it is correct, and to find the correct
    substitution that will convert the term into the pattern.

    Parameters
    ----------
    rule : RewriteRule
    syms : iterable
        Iterable of subterms that match a corresponding variable.

    Returns
    -------
    A dictionary of {vars : subterms} describing the substitution to make the
    pattern equivalent with the term. Returns `None` if the match is
    invalid.z/length of varlist doesn't match length of syms.N)r:   rH   ÚRuntimeErrorÚzip)rK   rP   r   ZvarlistÚvÚsr   r   r   rM     s    rM   N)Z
__future__r   r   r   Úcollectionsr   Z	dask.corer   r   r   r   Úobjectr   r$   rI   r   r*   r)   r0   rC   rX   rY   rU   rL   rM   r   r   r   r   Ú<module>   s$   <H &