o
    `i:                     @   s   d Z ddlmZmZmZmZ ddlmZ edZG dd dZ	ddd	Z
G d
d dZdeeee f dedefddZdd Zdd Zdd Zdd ZdS )a
  

Indexing one array with the other(s).

Concept for discussion.

Notation targets hard cases, not simple ones, like indexing of 1d-array with another 1d-array
(notation supports that, but you can't simplify arr[ind], and there is no reason to)

Examples

1. query for every token in sequence a token in the image. Images and sequences are paired
   einindex('b t c <- b h w c, [h, w] b t', arr_bhwc, [h_indices_bt, w_indices_bt])

   this is equivalent, so you can pass indexers idependently or together
   einindex('b t c <- b h w c, [h, w] b t', arr_bhwc, np.asarray([h_indices_bt, w_indices_bt]))

   after some thinking I decided that having first axis for indexing variable is not too restrictive,
   but should simplify mapping of such cases.
   For this reason [...] part should always go first in indexer.

   This makes the largest difference with einindex https://github.com/malmaud/einindex,
   which has almost identical grammar, but puts special dimension last, while we put it first.
   This trick allows naturally decomposing multiindex into individual dimensions or visa versa.


2. query for every token in the video the most suitable word in a (matching) sentence
   einindex('b t h w <- seq b, [seq] t b h w', arr_tbc, [t_indices_bhw])

   note, that only one indexer is used, but still it has to be enclosed in the list.
   That's a price for being generic. Alternatively leading singleton dimension can be added.


3. (not supported now, future planning)
   for every timeframe in a video, find the token with the highest norm (across h and w), and compose a new stack of them
   indices_2bt = argmax(x_bthwc.norm(dim=-1), 'b t h w -> [h, w] b t')
   selected_embeddings_btc = einindex('b t c <- b t h w c, [h, w] b t', x_bthwc, indices_2bt)

   while currently question is around 'how do we index',
   it is important to pre-align that with a question 'what are natural ways to get indices'.
   Most common are min/max. less common options: topk (works here), random sampling.



Some important properties of this notation:
- support for multiple indexers, including using a single tensor to keep multiple indexers
- 'batch' indexing, when some axes of indexer and array should be matched
- universal (one-indexing-to-rule-them-all)
- extensible for (named) ellipses, including variadic number of indexers
- extensible for einops-style compositions and decompositions
- extensible for outer indexing when indexers are not aligned

Current implementation based on python array api and uses loops,
because no appropriate indexing available in the standard.

    )ListUnionTypeVarTuple)EinopsErrorTc                   @   sV   e Zd Zdee deee  fddZdeeef fddZdeeef fdd	Z	d
S )CompositionDecompositiondecomposed_shapecomposed_shapec                    sX   g |D ]} | qt fddD | _tfdd D | _|| _ | _d S )Nc                       g | ]}  |qS  index.0x)r	   r   Z/home/ubuntu/SoloSpeech/.venv/lib/python3.10/site-packages/einops/experimental/indexing.py
<listcomp>K       z5CompositionDecomposition.__init__.<locals>.<listcomp>c                    r   r   r   r   )
flat_shaper   r   r   L   r   )extendtuplecompose_transpositiondecompose_transpositionr
   r	   )selfr	   r
   r   r   )r	   r   r   __init__B   s   
z!CompositionDecomposition.__init__known_axes_lengthsc                 C   s   |  }|j}g }t| jD ]C\}}d }d}	|D ]}
|
|v r%|	||
 9 }	q|d u r,|
}qtd|d u r=|| |	ks<J n|| |	 ||< |D ]	}|||  qGq|||}||| jS )N   zCan't infer the size)	__array_namespace__shape	enumerater
   r   appendreshapepermute_dimsr   )r   r   r   xpr   r   i
axis_groupunknown_axis_nameknown_sizes_prod	axis_nameaxisr   r   r   	decomposeP   s(   z"CompositionDecomposition.decomposec           	      C   s   |  }t|j| jD ]\}}||v r|| |ksJ q|||< q||| j}g }| jD ]}d}|D ]}||| 9 }q3|| q-||t	|S )Nr   )
r   zipr   r	   r#   r   r
   r!   r"   r   )	r   r   r   r$   axis_lenr)   	new_shaper&   composed_axis_sizer   r   r   composem   s   

z CompositionDecomposition.composeN)
__name__
__module____qualname__r   strr   dictintr+   r0   r   r   r   r   r   A   s    

r   Nc                 C   s4   | j || j|d}dg| }|||< | ||}|S )Ndtypedevicer   )arangeint64r"   )r$   n_axesr*   r-   r9   r   r   r   r   r   arange_at_position   s
   
r=   c                   @   s8   e Zd ZdefddZdedeeee f fddZdS )	IndexingFormulapatternc                    sr  || _ |d\}}|d}|d| ||d d }}| }|ds+J d|d}|d}||d | }	||d d }
| | _| | _dd	 |	dD | _|
 | _d
| jfd| jfd| j| j ffD ]\}}t	t
|t	|krt| d| dqp| j| j| j| jg}t
 }|D ]}|| qg | _g | _g | _g | _|D ]L t fdd|D }|dkr| j  q|d rtd  |dkr| j  q|dkr| j  q|dkr| j  qt  d| t
| jt
| jksJ | j| _t| j| j| j | jgd| _t| j| j| j gd| _t| j| j| j | jgd| _dS )zT
        :param pattern: example 'b t c <- b hsel wsel c, [hsel, wsel] b t'
        z<-,Nr   [zIcomposition axis should go first in indexer (second argument) [h w] i j k]c                 S   s   g | ]}|  qS r   )stripr   r   r   r   r      s    z,IndexingFormula.__init__.<locals>.<listcomp>resultarrayindexerz
 pattern (z) contains a duplicated axisc                 3   s    | ]} |v V  qd S Nr   )r   gr*   r   r   	<genexpr>   s    z+IndexingFormula.__init__.<locals>.<genexpr>)FTTF   z Wrong usage of indexer variable )TTFT)TFFT)TTFFz is used incorrectly in r	   r
   )r?   splitr   rC   
startswithresult_axes_namesarray_axes_namesindexing_axes_namesindexer_other_axes_nameslensetr   updateindexer_axes
batch_axesresult_and_index_axesresult_and_array_axesr   r!   r   array_compositionindex_compositionresult_composition)r   r?   leftright	arg_splitarr_patternind_patterncomposition_startcomposition_endcompositionind_other_axes
group_namegroupaxis_groupsall_axespresencer   rI   r   r      sx   





zIndexingFormula.__init__arrindc              	      sx  i }|  }ttsfddtjd D D ]}t|jt| jks)J q| j|| d}|j	dgtd j |j
|jdtt| jd d d D ]\}}||||    ||| 9 }qR| jd d d D ]!}| j|}t|t| j||| |jd|  ||| 9 }qo| jd ksJ | j|| fddtjd D }	| j|	|}
|
S )	Nc                    s   g | ]} |d f qS ).r   r   r%   )rl   r   r   r      s    z6IndexingFormula.apply_to_array_api.<locals>.<listcomp>r   r   r7   )r*   r-   r9   c                    s    g | ]} | d d f qS rG   r   rm   )arr_2d
full_indexr   r   r     s     )r   
isinstancelistranger   rS   rR   rZ   r0   zerosr;   r9   r,   rQ   rW   r   r=   r[   stackr\   r+   )r   rk   rl   known_axes_sizesr$   rF   shiftr)   axis_id	result_2drD   r   )ro   rp   rl   r   apply_to_array_api   s@   
$"$z"IndexingFormula.apply_to_array_apiN)	r1   r2   r3   r4   r   r   r   r   rz   r   r   r   r   r>      s    "Vr>   rl   r?   rk   c                C   s   t | }|||S )zX
    Demonstrates how einindex should work.
    Supports data-api compliant arrays.
    )r>   rz   )r?   rk   rl   formular   r   r   einindex  s   r|   c                  C   s  dd l m}  | d}| |d}tg dddgddggd	}|j|i d
jdks+J tg dddgg ddggd	|i }|jdksEJ | | |d| |dksVJ tg dddgdgddggd	}| d}| |d}i }|||}|||}| ||ksJ d S )Nr      )rK            )abcdr   r   r   r   rL   )r   )   #   )r   r   r   )rn   )r   r   er   r   r   iv  )rK   r~   r   r   r~   )	numpy.array_api	array_apir:   r"   r   r0   r   allr+   )npr   compyaxesx2r   r   r   "test_composition_and_decomposition   s4   
"
r   c                  C   s   dd l m}  | | dd}| dd }td||g}t|D ]\}}|||f || ks1J q!td| |d|g}t|D ]\}}|||f || ksQJ qAd S )	Nr   r   )r   r   r   r   zj <- i j, [i] jzj <- j i, [i] j)r   r   )r   r   r"   r:   r|   r    r#   )r   rk   rl   r   jr%   r   r   r   r   test_simple_indexing?  s   r   c                  C   s8  dd l m}  t| ddd
 d t| dddd  t| dddd	  t| ddd
d  }| | ddd }| | ddd }td|||g}| ||g}td||}| ||ks_J |d }tdD ])}tdD ]"}	td
D ]}
|||	f }|||	f }|||||
f ||
|	|f< qsqmqg| ||ksJ d S )Nr      rK     r   r~   d   r   
   r   r   )rK   r~   zc t b <- b h w c, [h, w] b t)	r   r   r=   r"   r:   r|   ru   r   rs   )r   embedding_bhwchindices_btwindices_btrD   hw_indices_btresult2result_manualr   tr   hwr   r   r   test_multidimensional_indexingN  s2   r   c                  C   s6  dd l m}  d\}}}d}d}d}t| dd|
 d t| dd|d  t| dd	|d
  t| dd|d  }| | || | | ||||f| }td||g}	|	d }
t|D ]5}t|D ].}t|D ]'}t|D ] }t|D ]}|||||f }|||||f |
|||||f< qrqlqfq`qZ| |	|
ksJ d S )Nr   )rK   r~   r   r   r   	   r   r   r   rK   r   r~   z!g b c h w <- g t b c, [t] g b h w)r   r   r=   r"   r:   r|   rs   r   )r   Cr   BGHWarr_gtbct_indices_gbhwrD   r   rH   r   r   r   r   r   r   r   r   test_reverse_indexingq  s8   
* r   rG   )__doc__typingr   r   r   r   einopsr   r   r   r=   r>   r4   r|   r   r   r   r   r   r   r   r   <module>   s    9
@ "	#