U
    /e2                     @  s  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	Z
d dlmZ d dlmZ d dlmZmZmZ d d	lmZmZ d d
lmZmZ d dlmZ d dlmZmZmZ d dlm Z  eeeefZ!eefZ"ddefdfddZ#dd Z$ddddZ%dd Z&dd Z'dd Z(dd Z)dS )    )annotations)zip_longest)Integral)AnyCallableN)config)getitem)gettergetter_inlinegetter_nofancy)
fuse_rootsoptimize_blockwise)flattenreverse_dict)HighLevelGraph)SubgraphCallablefuseinline_functions)ensure_dictTc           	      K  s   t |ttfs|g}tt|}t | ts>tjt| | dd} t| |d} t| |d} | 	t|} t
ddkrv| S |  }t| } |dk	r|}t| |}t| || |pg  ||d\} }|rt| |||d} t| S )	zOptimize dask for array computation

    1.  Cull tasks not necessary to evaluate keys
    2.  Remove full slicing, e.g. x[:]
    3.  Inline fast functions like getitem and np.transpose
     )dependencies)keyszoptimization.fuse.activeFN)Zrename_keys)r   fast_functions)
isinstancelistsetr   r   Zfrom_collectionsidr   r   Zcullr   getZget_all_dependenciesr   	hold_keysr   r   optimize_slices)	dskr   Z	fuse_keysr   Zinline_functions_fast_functionsZrename_fused_keyskwargsr   Zholdr   r   ;/tmp/pip-unpacked-wheel-dbjnr7gq/dask/array/optimization.pyoptimize   s:    


r#   c                 C  s   t |}dd |  D }t|}|D ]}|| }|D ]~}| | }t|r6zFt|| dkrtt|| }	| |	 }
t|
s|
| kr|	}qLqqLW n ttfk
r   Y nX |	| q6q&|S )a"  Find keys to avoid fusion

    We don't want to fuse data present in the graph because it is easier to
    serialize as a raw value.

    We don't want to fuse chains after getitem/GETTERS because we want to
    move around only small pieces of data, rather than the underlying arrays.
    c                 S  s$   h | ]\}}t |ttfkr|qS r   )typetuplestr).0kvr   r   r"   	<setcomp>]   s      zhold_keys.<locals>.<setcomp>   )
r   itemsr   _is_getter_tasklennextiter
IndexError	TypeErrorappend)r    r   Z
dependentsdatar   ZdatdepsdepZtaskZnew_depZnew_taskr   r   r"   r   S   s&    	r   z3tuple[Callable, Any, Any, bool, bool | None] | None)returnc                 C  s   t | tk	rdS | d }d}|tkr*|}nVt|trt|jdkrtt|j	 }t |tkrt|dkr|d tkr|d }|dkrdS t| }|dkr|| d | d |t
k	dfS |dkr|f| dd S dS )a  Check if a value in a Dask graph looks like a getter.

    1. Is it a tuple with the first element a known getter.
    2. Is it a SubgraphCallable with a single element in its
       dsk which is a known getter.

    If a getter is found, it returns a tuple with (getter, array, index, asarray, lock).
    Otherwise it returns ``None``.

    TODO: the second check is a hack to allow for slice fusion between tasks produced
    from blockwise layers and slicing operations. Once slicing operations have
    HighLevelGraph layers which can talk to Blockwise layers this check *should* be
    removed, and we should not have to introspect SubgraphCallables.
    Nr   r+            )r$   r%   GETTERSr   r   r.   r    r/   r0   valuesr   )valuefirstr   r)   lengthr   r   r"   r-   y   s$    $r-   c              	     s  t tjf |  } |  D ]\}}t| }r|\}}}}}t| }	rf|	\}
}}}}|rj||k	rjqft|tkt|tkkrqft|tkr|| }t|t|krt	dd |D rqf|
t
krt	 fdd|D rqfn*|
t
krt| ksft| krqfz t||}|
tkr(tn|
}W n tk
rJ   Y qfY nX |||  }}}||O }q>|tkrt|tkr|js|jdkr|jdkst|tkrtdd |D r|| |< q|tks|r|s|||f| |< q|||||f| |< q| S )zOptimize slices

    1.  Fuse repeated slices, like x[5:][2:6] -> x[7:11]
    2.  Remove full slices, like         x[:] -> x

    See also:
        fuse_slice_dict
    c                 s  s   | ]}|d kV  qd S Nr   r'   ir   r   r"   	<genexpr>   s     z"optimize_slices.<locals>.<genexpr>c                 3  s   | ]}t | V  qd S r@   )r   rA   Zfancy_ind_typesr   r"   rC      s    Nc                 s  s6   | ].}t |tko,|j o,|jd ko,|jd kV  qd S r@   )r$   slicestartstopstep)r'   sr   r   r"   rC      s   )r   npZndarraycopyr,   r-   r$   r%   r.   anyr   
fuse_slicer
   r	   NotImplementedErrorGETNOREMOVErE   rF   rG   rH   allr   )r    r(   r)   Za_taskr   aZa_indexZ	a_asarrayZa_lockZb_taskf2bZb_indexZ	b_asarrayZb_lockindicesZc_indexr   rD   r"   r      sh    	
"







	
r   c                 C  s`   | j | j| j  }}}|dkr"d}|dkr.d}|dk sN|dk sN|dk	rT|dk rTt t|||S )zrReplace Nones in slices with integers

    >>> normalize_slice(slice(None, None, None))
    slice(0, None, 1)
    Nr   r+   )rF   rG   rH   rN   rE   )rI   rF   rG   rH   r   r   r"   normalize_slice   s     rU   c                 C  s>   t | |td dD ]&\}}t|tk	rt|trtdqd S )N)	fillvaluezCan't handle normal indexing with integers and fancy indexing if the integers and fancy indices don't align with the same dimensions.)r   rE   r$   r   r   r   rN   )Zfancynormalfnr   r   r"   #check_for_nonfusible_fancy_indexing  s
    rZ   c           
        s   dkr$t |tr$|tddkr$dS t  tr6t  t |trHt|}t  trzt |trz|dk rjt  j| j  S t  trt |tr j j|j  }|jdk	r j j|j  }nd} jdk	r|dk	rt j|}n j} j|j }|dkrd}t|||S t |t	r0 fdd|D S t  t	rTt |ttfrT | S t  t
rrt |t
sr|f}t  t
rt |t
rtdd  D }tdd |D }|r|rtd	n"|rt | n|rt|  d}t	 }tt D ]z}	t  |	 ts|t|kr0| |	  q|| dkrT|d |d7 }q0|t |	 ||  |d7 }q|t|k r|||  |d7 }qxt
|S t dS )
a  Fuse stacked slices together

    Fuse a pair of repeated slices into a single slice:

    >>> fuse_slice(slice(1000, 2000), slice(10, 15))
    slice(1010, 1015, None)

    This also works for tuples of slices

    >>> fuse_slice((slice(100, 200), slice(100, 200, 10)),
    ...            (slice(10, 15), [5, 2]))
    (slice(110, 115, None), [150, 120])

    And a variety of other interesting cases

    >>> fuse_slice(slice(1000, 2000), 10)  # integers
    1010

    >>> fuse_slice(slice(1000, 2000, 5), slice(10, 20, 2))
    slice(1050, 1100, 10)

    >>> fuse_slice(slice(1000, 2000, 5), [1, 2, 3])  # lists
    [1005, 1010, 1015]

    >>> fuse_slice(None, slice(None, None))  # doctest: +SKIP
    None
    Nr   r+   c                   s   g | ]}t  |qS r   )rM   )r'   ZbbrQ   r   r"   
<listcomp>M  s     zfuse_slice.<locals>.<listcomp>c                 s  s   | ]}t |tV  qd S r@   r   r   r'   itemr   r   r"   rC   Y  s     zfuse_slice.<locals>.<genexpr>c                 s  s   | ]}t |tV  qd S r@   r]   r^   r   r   r"   rC   Z  s     z#Can't handle multiple list indexing)r   rE   rU   r   rN   rF   rH   rG   minr   r%   rL   rZ   ranger.   r3   rM   )
rQ   rS   rF   rG   rH   Za_has_listsZb_has_listsjresultrB   r   r[   r"   rM     sl     







rM   )*
__future__r   	itertoolsr   Znumbersr   typingr   r   ZnumpyrJ   Zdaskr   Zdask.array.chunkr   Zdask.array.corer	   r
   r   Zdask.blockwiser   r   Z	dask.corer   r   Zdask.highlevelgraphr   Zdask.optimizationr   r   r   Z
dask.utilsr   r;   rO   r#   r   r-   r   rU   rZ   rM   r   r   r   r"   <module>   s2   
9&+M