B
    T\                 @   s`   d dl Z d dlZd dlZddlmZmZ ddlmZ ddlm	Z	 ddl
m
Z dd Z
d	d
 ZdS )    N   )baseutils)unpack_collections)HighLevelGraph)	blockwisec                s  | dd}| dd}| dd}| dd | di }| dd}tt|t|krztd	d
d t| D t|dd |ddd D  t|pd }	|	rtd|	ddlm}
m	}m
} |dkrtd|r|| \}nDtdd td|D dd d\}tt|j|ddd }x0| D ]$\}}t|tsR|f}||< q8W tt||ddd }xL|D ]D\}t|dr~tdr~|jtkr~td|jf q~W dd |D }g }g }g }xZ|D ]R\}dkr||}t|\}}|| n|| |j}||f qW i }x<| D ]0\}}||}t|\}}|| |||< qJW |sd|pt| dtj| |||f|f }t| ||f||||d|}tj |||| d }fd!d|D } rxt!|D ]\} krt"  rDtt#  || ||< nbt  t$j%rxt fd"d#|| D ||< n.t  ttfrt  ||< nt&d$qW t|}|
||||d%S )&a   Tensor operation: Generalized inner and outer products

    A broad class of blocked algorithms and patterns can be specified with a
    concise multi-index notation.  The ``blockwise`` function applies an in-memory
    function across multiple blocks of multiple inputs in a variety of ways.
    Many dask.array operations are special cases of blockwise including
    elementwise, broadcasting, reductions, tensordot, and transpose.

    Parameters
    ----------
    func : callable
        Function to apply to individual tuples of blocks
    out_ind : iterable
        Block pattern of the output, something like 'ijk' or (1, 2, 3)
    *args : sequence of Array, index pairs
        Sequence like (x, 'ij', y, 'jk', z, 'i')
    **kwargs : dict
        Extra keyword arguments to pass to function
    dtype : np.dtype
        Datatype of resulting array.
    concatenate : bool, keyword only
        If true concatenate arrays along dummy indices, else provide lists
    adjust_chunks : dict
        Dictionary mapping index to function to be applied to chunk sizes
    new_axes : dict, keyword only
        New indexes and their dimension lengths

    Examples
    --------
    2D embarrassingly parallel operation from two arrays, x, and y.

    >>> z = blockwise(operator.add, 'ij', x, 'ij', y, 'ij', dtype='f8')  # z = x + y  # doctest: +SKIP

    Outer product multiplying x by y, two 1-d vectors

    >>> z = blockwise(operator.mul, 'ij', x, 'i', y, 'j', dtype='f8')  # doctest: +SKIP

    z = x.T

    >>> z = blockwise(np.transpose, 'ji', x, 'ij', dtype=x.dtype)  # doctest: +SKIP

    The transpose case above is illustrative because it does same transposition
    both on each in-memory block by calling ``np.transpose`` and on the order
    of the blocks themselves, by switching the order of the index ``ij -> ji``.

    We can compose these same patterns with more variables and more complex
    in-memory functions

    z = X + Y.T

    >>> z = blockwise(lambda x, y: x + y.T, 'ij', x, 'ij', y, 'ji', dtype='f8')  # doctest: +SKIP

    Any index, like ``i`` missing from the output index is interpreted as a
    contraction (note that this differs from Einstein convention; repeated
    indices do not imply contraction.)  In the case of a contraction the passed
    function should expect an iterable of blocks on any array that holds that
    index.  To receive arrays concatenated along contracted dimensions instead
    pass ``concatenate=True``.

    Inner product multiplying x by y, two 1-d vectors

    >>> def sequence_dot(x_blocks, y_blocks):
    ...     result = 0
    ...     for x, y in zip(x_blocks, y_blocks):
    ...         result += x.dot(y)
    ...     return result

    >>> z = blockwise(sequence_dot, '', x, 'i', y, 'i', dtype='f8')  # doctest: +SKIP

    Add new single-chunk dimensions with the ``new_axes=`` keyword, including
    the length of the new dimension.  New dimensions will always be in a single
    chunk.

    >>> def f(x):
    ...     return x[:, None] * np.ones((1, 5))

    >>> z = blockwise(f, 'az', x, 'a', new_axes={'z': 5}, dtype=x.dtype)  # doctest: +SKIP

    If the applied function changes the size of each chunk you can specify this
    with a ``adjust_chunks={...}`` dictionary holding a function for each index
    that modifies the dimension size in that index.

    >>> def double(x):
    ...     return np.concatenate([x, x])

    >>> y = blockwise(double, 'ij', x, 'ij',
    ...               adjust_chunks={'i': lambda n: 2 * n}, dtype=x.dtype)  # doctest: +SKIP

    Include literals by indexing with None

    >>> y = blockwise(add, 'ij', x, 'ij', 1234, None, dtype=x.dtype)  # doctest: +SKIP

    See Also
    --------
    top - dict formulation of this function, contains most logic
    nameNtokendtypeadjust_chunksnew_axesalign_arraysTz-Repeated elements not allowed in output indexc             S   s   g | ]\}}|d kr|qS )    ).0kvr   r   3lib/python3.7/site-packages/dask/array/blockwise.py
<listcomp>w   s    zblockwise.<locals>.<listcomp>c             S   s"   h | ]}|d k	r|D ]}|qqS )Nr   )r   argar   r   r   	<setcomp>y   s    zblockwise.<locals>.<setcomp>r   r   r   zUnknown dimension)Arrayunify_chunksnormalize_argz"Must specify dtype of output arrayc             S   s    g | ]\}}|d k	r||fqS )Nr   )r   r   ir   r   r   r      s    c             S   s   t | d S )Nr   )len)Zair   r   r   <lambda>   s    zblockwise.<locals>.<lambda>)keyndim__len__z1Index string %s does not match array dimension %dc             S   s"   i | ]\}}|d k	r|j |jqS )N)	numblocksr   )r   r   indr   r   r   
<dictcomp>   s    zblockwise.<locals>.<dictcomp>z%s-%s_)r!   dependenciesr   )r%   c                s   g | ]} | qS r   r   )r   r   )chunkssr   r   r      s    c             3   s   | ]}  V  qd S )Nr   )r   r$   )r   r"   r   r   	<genexpr>   s    zblockwise.<locals>.<genexpr>z4adjust_chunks values must be callable, int, or tuple)r
   )'popr   set
ValueErrortoolzZfrequenciesitemsZcorer   r   r   max	partitiondictzipchunks
isinstancetuplelisthasattrr   r   extendappendr   r   funcnamestripr   tokenizecore_blockwiser   Zfrom_collections	enumeratecallablemapnumbersZIntegralNotImplementedError)funcZout_indargskwargsoutr	   r
   r   r   newr   r   r   Zarraysr   r   r   Zargindsr!   r%   Z
argindsstrr   collectionsZkwargs2Zgraphr1   r   r   )r   r&   r"   r   r      s    a,
(



"r   c              O   s   t d t| |S )Nz.The da.atop function has moved to da.blockwise)warningswarnr   )rB   rC   r   r   r   atop   s    
rI   )r?   rG   r+    r   r   Zdelayedr   Zhighlevelgraphr   r   r;   rI   r   r   r   r   <module>   s    @