§ <ˆjœÊãó—dZddlZddlZddlmZddlmZmZddlm Z ddgZ dZ e e ¦«Z d „Zd „Zd „Zd „Zd „Zd„Zd„Zd„Zd„Zdddœd„Ze ed¬¦«dddœd„¦«Zdddœd„Ze ed¬¦«dddœd„¦«ZdS)z& Implementation of optimized einsum. éN)Úc_einsum)Ú asanyarrayÚ tensordot)Úarray_function_dispatchÚeinsumÚ einsum_pathÚ4abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZcó`—t||¦«}td|dz ¦«}|r|dz }||zS)a› Computes the number of FLOPS in the contraction. Parameters ---------- idx_contraction : iterable The indices involved in the contraction inner : bool Does this contraction require an inner product? num_terms : int The number of terms in a contraction size_dictionary : dict The size of each of the indices in idx_contraction Returns ------- flop_count : int The total number of FLOPS required for the contraction. Examples -------- >>> _flop_count('abc', False, 1, {'a': 2, 'b':3, 'c':5}) 30 >>> _flop_count('abc', True, 2, {'a': 2, 'b':3, 'c':5}) 60 é)Ú_compute_size_by_dictÚmax)Úidx_contractionÚinnerÚ num_termsÚsize_dictionaryÚ overall_sizeÚ op_factors úL/opt/cloudlinux/venv/lib64/python3.11/site-packages/numpy/core/einsumfunc.pyÚ _flop_countrsB€õ>)¨¸/ÑJÔJ€LÝ�A�y 1‘}Ñ%Ô%€IØ ðØ�Q‰ˆ à ˜)Ñ #Ð#ócó*—d}|D] }|||z}Œ|S)a  Computes the product of the elements in indices based on the dictionary idx_dict. Parameters ---------- indices : iterable Indices to base the product on. idx_dict : dictionary Dictionary of index sizes Returns ------- ret : int The resulting product. Examples -------- >>> _compute_size_by_dict('abbc', {'a': 2, 'b':3, 'c':5}) 90 r ©)ÚindicesÚidx_dictÚretÚis rr r 8s-€ð. €CØ ððˆØ ˆx˜Œ{шˆØ €Jrcó—t¦«}| ¦«}g}t|¦«D])\}}||vr||z}Œ| |¦«||z}Œ*||z}||z } | |¦«||| |fS)a  Finds the contraction for a given set of input and output sets. Parameters ---------- positions : iterable Integer positions of terms used in the contraction. input_sets : list List of sets that represent the lhs side of the einsum subscript output_set : set Set that represents the rhs side of the overall einsum subscript Returns ------- new_result : set The indices of the resulting contraction remaining : list List of sets that have not been contracted, the new set is appended to the end of this list idx_removed : set Indices removed from the entire contraction idx_contraction : set The indices used in the current contraction Examples -------- # A simple dot product test case >>> pos = (0, 1) >>> isets = [set('ab'), set('bc')] >>> oset = set('ac') >>> _find_contraction(pos, isets, oset) ({'a', 'c'}, [{'a', 'c'}], {'b'}, {'a', 'b', 'c'}) # A more complex case with additional terms in the contraction >>> pos = (0, 2) >>> isets = [set('abd'), set('ac'), set('bdc')] >>> oset = set('ac') >>> _find_contraction(pos, isets, oset) ({'a', 'c'}, [{'a', 'c'}, {'a', 'c'}], {'b', 'd'}, {'a', 'b', 'c', 'd'}) )ÚsetÚcopyÚ enumerateÚappend) Ú positionsÚ input_setsÚ output_setÚ idx_contractÚ idx_remainÚ remainingÚindÚvalueÚ new_resultÚ idx_removeds rÚ_find_contractionr,Us«€õV‘5”5€LØ—’Ñ"Ô"€JØ€IÝ  Ñ+Ô+ð ð ‰ ˆˆUØ �)Ð Ð Ø ˜EÑ !ˆLˆLà × Ò ˜UÑ #Ô #Ð #Ø ˜%Ñ ˆJˆJà˜lÑ*€JØ *Ñ,€KØ ×Ò�ZÑ Ô Ð à ˜  ;° Ð =Ð=rc ó—dg|fg}tt|¦«dz ¦«D�]}g}|D]¬}|\}} } tjtt|¦«|z ¦«d¦«D]q} t | | |¦«} | \} }}}t | |¦«}||krŒ1|t ||t| ¦«|¦«z}| | gz}| |||f¦«ŒrŒ­|r|}Œ¹t|d„¬¦«d}|ttt|¦«|z ¦«¦«gz }|cSt|¦«dkr*ttt|¦«¦«¦«gSt|d„¬¦«d}|S)aŸ Computes all possible pair contractions, sieves the results based on ``memory_limit`` and returns the lowest cost path. This algorithm scales factorial with respect to the elements in the list ``input_sets``. Parameters ---------- input_sets : list List of sets that represent the lhs side of the einsum subscript output_set : set Set that represents the rhs side of the overall einsum subscript idx_dict : dictionary Dictionary of index sizes memory_limit : int The maximum number of elements in a temporary array Returns ------- path : list The optimal contraction order within the memory limit constraint. Examples -------- >>> isets = [set('abd'), set('ac'), set('bdc')] >>> oset = set() >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4} >>> _optimal_path(isets, oset, idx_sizes, 5000) [(0, 2), (0, 1)] rr écó—|dS©Nrr©Úxs rúz_optimal_path..Ìs €°1°Q´4€r©Úkeycó—|dSr0rr1s rr3z_optimal_path..Ôs €¨1¨Q¬4€r) ÚrangeÚlenÚ itertoolsÚ combinationsr,r rr!ÚminÚtuple)r#r$rÚ memory_limitÚ full_resultsÚ iterationÚ iter_resultsÚcurrÚcostr"r'ÚconÚcontr*Únew_input_setsr+r%Únew_sizeÚ total_costÚnew_posÚpaths rÚ _optimal_pathrJ‘sÊ€ð>˜˜JÐ'Ð(€LÝ�3˜z™?œ?¨QÑ.Ñ/Ô/ðñˆ ؈ ð!ð Kð KˆDØ)-Ñ &ˆD�)˜YÝ Ô-­eµC¸ ±O´OÀiÑ4OÑ.PÔ.PÐRSÑTÔTð Kð K�õ)¨¨i¸ÑDÔD�ØHLÑE� ˜N¨K¸õ1°¸XÑFÔF�ؘlÒ*Ð*Øð#¥[°¸{ÍCÐPSÉHÌHÐV^Ñ%_Ô%_Ñ_� Ø# s eÑ+�Ø×#Ò# Z°¸.Ð$IÑJÔJÐJÐJð Kð$ ð Ø'ˆLˆLå�|¨¨Ð8Ñ8Ô8¸Ô;ˆDØ •U�5¥ Z¡¤°9Ñ!<Ñ=Ô=Ñ>Ô>Ð?Ñ ?ˆD؈KˆKˆKõ ˆ<ÑÔ˜AÒÐÝ•e�C  ™OœOÑ,Ô,Ñ-Ô-Ð.Ð.å ˆ|  Ð 0Ñ 0Ô 0°Ô 3€DØ €Krc󇇗t|‰|¦«}|\}} } } t|‰¦«} | |krdSˆˆfd„|D¦«} t| ¦«| z }t| | t |¦«‰¦«}| |f}||z|krdS||| gS)aáCompute the cost (removed size + flops) and resultant indices for performing the contraction specified by ``positions``. Parameters ---------- positions : tuple of int The locations of the proposed tensors to contract. input_sets : list of sets The indices found on each tensors. output_set : set The output indices of the expression. idx_dict : dict Mapping of each index to its size. memory_limit : int The total allowed size for an intermediary tensor. path_cost : int The contraction cost so far. naive_cost : int The cost of the unoptimized expression. Returns ------- cost : (int, int) A tuple containing the size of any indices removed, and the flop cost. positions : tuple of int The locations of the proposed tensors to contract. new_input_sets : list of sets The resulting new list of indices if this proposed contraction is performed. Nc3óD•K—|]}t‰|‰¦«V—ŒdS©N©r )Ú.0Úprr#s €€rú z._parse_possible_contraction..s2øèè€ÐSÐSÀAÕ& z°!¤}°hÑ?Ô?ÐSÐSÐSÐSÐSÐSr)r,r Úsumrr8)r"r#r$rr=Ú path_costÚ naive_costÚcontractÚ idx_resultrEr+r%rFÚ old_sizesÚ removed_sizerBÚsorts ` ` rÚ_parse_possible_contractionrZ×sÃøø€õB! ¨J¸ ÑCÔC€HØ”> HÑ,€Lõ �| [µ#°i±.´.À(Ñ KÔ K€DØ ˆM˜4Ð €Dð �DјJÒ&Ð&؈tð �)˜^Ð ,Ð,rcó—|d}|\}}g}|D]ö\}\}}} ||vs||vrŒ| |t||k¦«z t||k¦«z =| |t||k¦«z t||k¦«z =|  d|dd¦«|t||k¦«z t||k¦«z |t||k¦«z t||k¦«z f} | || | f¦«Œ÷|S)aUpdate the positions and provisional input_sets of ``results`` based on performing the contraction result ``best``. Remove any involving the tensors contracted. Parameters ---------- results : list List of contraction results produced by ``_parse_possible_contraction``. best : list The best contraction of ``results`` i.e. the one that will be performed. Returns ------- mod_results : list The list of modified results, updated with outcome of ``best`` contraction. r éÿÿÿÿr.)ÚintÚinsertr!) ÚresultsÚbestÚbest_conÚbxÚbyÚ mod_resultsrBr2ÚyÚcon_setsÚmod_cons rÚ_update_other_resultsrhs€ð$�AŒw€HØ �F€BˆØ€Kà")ð 6ð 6ш‰fˆq�!�hð �ˆ=ˆ=˜A ˜M˜MØ ð �R�#˜b 1šf™+œ+Ñ%­¨B°ªF© ¬ Ñ3Ð 4Ø �R�#˜b 1šf™+œ+Ñ%­¨B°ªF© ¬ Ñ3Ð 4Ø�Š˜˜D œG BœKÑ(Ô(Ð(ð•c˜!˜bš&‘k”k‘/¥C¨¨Bª¡K¤KÑ/°µS¸¸Rº±[´[±Å3ÀqÈ2ÂvÁ;Ä;Ñ1NÐNˆØ×Ò˜D '¨8Ð4Ñ5Ô5Ð5Ð5à Ðrc óü‡—t|¦«dkrdgSt|¦«dkrdgSttt|¦«¦«||¦«}|\}}}}t||t|¦«|¦«} t jtt|¦«¦«d¦«} g} d} g} tt|¦«dz ¦«D�]§}| D]\}||d ||d¦«rŒ0t|||||| | ¦«}|�|  |¦«Œ]t| ¦«dkr¯t jtt|¦«¦«d¦«D].}t|||||| | ¦«}|�|  |¦«Œ/t| ¦«dkr>|  ttt|¦«¦«¦«¦«n…t| d„¬¦«}t| |¦«} |d}t|¦«dz Šˆfd „t‰¦«D¦«} |  |d¦«| |ddz } �Œ©| S) a§ Finds the path by contracting the best pair until the input list is exhausted. The best pair is found by minimizing the tuple ``(-prod(indices_removed), cost)``. What this amounts to is prioritizing matrix multiplication or inner product operations, then Hadamard like operations, and finally outer operations. Outer products are limited by ``memory_limit``. This algorithm scales cubically with respect to the number of elements in the list ``input_sets``. Parameters ---------- input_sets : list List of sets that represent the lhs side of the einsum subscript output_set : set Set that represents the rhs side of the overall einsum subscript idx_dict : dictionary Dictionary of index sizes memory_limit : int The maximum number of elements in a temporary array Returns ------- path : list The greedy contraction order within the memory limit constraint. Examples -------- >>> isets = [set('abd'), set('ac'), set('bdc')] >>> oset = set() >>> idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4} >>> _greedy_path(isets, oset, idx_sizes, 5000) [(0, 2), (0, 1)] r )rr.)rr rNcó—|dSr0rr1s rr3z_greedy_path..‹s €°Q°q´T€rr4c3ó •K—|]}|‰fV—Œ dSrMr)rOrÚnew_tensor_poss €rrQz_greedy_path..”s(øèè€ÐHÐH¨Q�a˜Ð(ÐHÐHÐHÐHÐHÐHr) r8r,r7rr9r:Ú isdisjointrZr!r<r;rh)r#r$rr=rUrVrEr+r%rTÚ comb_iterÚknown_contractionsrSrIr?r"Úresultr`rls @rÚ _greedy_pathrq8s¥ø€õH ˆ:�„˜!ÒÐØˆvˆ Ý ˆZ‰Œ˜AÒ Ð Øˆxˆõ!¥¥s¨:¡¤Ñ!7Ô!7¸ÀZÑPÔP€HØÀ ñLôL�àÐ%Ø&×-Ò-¨fÑ5Ô5Ð5øõÐ%Ñ&Ô&¨!Ò+Ð+Ø— ’ �E¥%­¨J©¬Ñ"8Ô"8Ñ9Ô9Ñ:Ô:Ð:Ø�õÐ%¨>¨>Ð:Ñ:Ô:ˆõ3Ð3EÀtÑLÔLÐð˜!”Wˆ ݘZ™œ¨1Ñ,ˆØHÐHÐHÐHµ%¸Ñ2GÔ2GÐHÑHÔHˆ ð � Š �D˜”GÑÔÐØ�T˜!”W˜Q”Zш ‰ à €Krcó°—t|¦«dkrdSt|¦«dkrdS|\}}t||z¦«D]b}| |¦«| |¦«}}|dks|dks ||zdkrdS||zdz t||v¦«krdSŒct|¦«}t|¦«} ||z } | |z } t|¦«} ||krdS|| krdS|| d…|d| …krdS|d| …|| d…krdS|| d…|| d…krdS|d| …|d| …krdS| r| sdSdS)a¸ Checks if we can use BLAS (np.tensordot) call and its beneficial to do so. Parameters ---------- inputs : list of str Specifies the subscripts for summation. result : str Resulting summation. idx_removed : set Indices that are removed in the summation Returns ------- type : bool Returns true if BLAS should and can be used, else False Notes ----- If the operations is BLAS level 1 or 2 and is not already aligned we default back to einsum as the memory movement to copy is more costly than the operation itself. Examples -------- # Standard GEMM operation >>> _can_dot(['ij', 'jk'], 'ik', set('j')) True # Can use the standard BLAS, but requires odd data movement >>> _can_dot(['ijj', 'jk'], 'ik', set('j')) False # DDOT where the memory is not aligned >>> _can_dot(['ijk', 'ikj'], '', set('ijk')) False rFr.r TN)r8rÚcountr]) Úinputsrpr+Ú input_leftÚ input_rightÚcÚnlÚnrÚset_leftÚ set_rightÚ keep_leftÚ keep_rightÚrss rÚ_can_dotr�sã€õX ˆ;ÑÔ˜1ÒÐØˆuõ ˆ6�{„{�aÒÐØˆuà$Ñ€J� å �˜kÑ)Ñ *Ô *ð ð ˆà×!Ò! !Ñ$Ô$ k×&7Ò&7¸Ñ&:Ô&:ˆBˆØ �ŠFˆF˜˜Qš˜ B¨¡G¨a¢K KØ�5�5ð �‰7�Q‰;�#˜a 6˜kÑ*Ô*Ò *Ð *Ø�5�5ð +õ�:‰Œ€HÝ�KÑ Ô €Iؘ;Ñ&€Iؘ[Ñ(€JÝ ˆ[Ñ Ô €Bð�[Ò Ð Øˆtð�9ÒÐØˆuð �2�#�$�$Ô˜; s¨ sÔ+Ò+Ð+؈tð�#�2�#„˜+ r c d dÔ+Ò+Ð+؈tð�2�#�$�$Ô˜;¨ s t tÔ,Ò,Ð,؈tð�#�2�#„˜+ c r cÔ*Ò*Ð*؈tð ð˜Jð؈uð ˆ4rcó¢ —t|¦«dkrtd¦«‚t|dt¦«rW|d dd¦«}d„|dd…D¦«}|D]"}|dvrŒ|t vrtd |z¦«‚Œ#�n�t |¦«}g}g}tt|¦«d z¦«D]R}| |  d¦«¦«| |  d¦«¦«ŒSt|¦«r|d nd}d „|D¦«}d}t|¦«dz }t|¦«D]l\} } | D]Y}|tur|d z }Œ tj |¦«}n"#t$r} td¦«| ‚d} ~ wwxYw|t |z }ŒZ| |kr|dz }Œm|�a|dz }|D]Y}|tur|d z }Œ tj |¦«}n"#t$r} td¦«| ‚d} ~ wwxYw|t |z }ŒZd|vsd|vr\| d¦«dkp| d¦«dk} | s| d¦«dkrtd¦«‚d|v�rÅ| dd¦« dd¦« dd¦«} t t t#| ¦«z ¦«}d |¦«}d}d|vr0| d¦«\}}| d¦«}d}n| d¦«}d}t|¦«D]ï\} } d| vræ|  d¦«dks|  d ¦«dkrtd¦«‚|| jdkrd}n0t+|| jd¦«}|t| ¦«dz z}||kr|}|dkrtd¦«‚|dkr|  d d¦«|| <ŒË|| d…}|  d |¦«|| <Œðd |¦«}|dkrd}n || d…}|r|d| d |¦«zz }nºd}| dd¦«}t/t#|¦«¦«D];}|t vrtd |z¦«‚| |¦«dkr||z }Œ>> np.random.seed(123) >>> a = np.random.rand(4, 4) >>> b = np.random.rand(4, 4, 4) >>> _parse_einsum_input(('...a,...a->...', a, b)) ('za,xza', 'xz', [a, b]) # may vary >>> _parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0])) ('za,xza', 'xz', [a, b]) # may vary rzNo input operandsú Úcó,—g|]}t|¦«‘ŒSr©r©rOÚvs rú z'_parse_einsum_input..+ó€Ð8Ð8Ð8 a•J˜q‘M”MÐ8Ð8Ð8rr Nz.,->z#Character %s is not a valid symbol.r.r\có,—g|]}t|¦«‘ŒSrr„r…s rr‡z'_parse_einsum_input..=rˆrz...z=For this input type lists must contain either int or Ellipsisú,ú->ú-ú>z%Subscripts can only contain one '->'.ú.TFézInvalid Ellipses.rzEllipses lengths do not match.z/Output character %s did not appear in the inputzDNumber of einsum subscripts must be equal to the number of operands.)r8Ú ValueErrorÚ isinstanceÚstrÚreplaceÚeinsum_symbolsÚlistr7r!Úpopr ÚEllipsisÚoperatorÚindexÚ TypeErrorrsÚeinsum_symbols_setrÚjoinÚsplitÚshaper ÚndimÚsorted)ÚoperandsÚ subscriptsÚsÚ tmp_operandsÚ operand_listÚsubscript_listrPÚ output_listÚlastÚnumÚsubÚeÚinvalidÚusedÚunusedÚ ellipse_indsÚlongestÚ input_tmpÚ output_subÚsplit_subscriptsÚout_subÚ ellipse_countÚrep_indsÚ out_ellipseÚoutput_subscriptÚtmp_subscriptsÚ normal_indsÚinput_subscriptsÚchars rÚ_parse_einsum_inputr½ s�€õ6 ˆ8�}„}˜ÒÐÝÐ,Ñ-Ô-Ð-å�(˜1”+�sÑ#Ô#ð04ؘa”[×(Ò(¨¨bÑ1Ô1ˆ Ø8Ð8¨8°A°B°B¬<Ð8Ñ8Ô8ˆðð Lð LˆAØ�Fˆ{ˆ{ØØ�Ð&Ð&Ý Ð!FÈÑ!JÑKÔKÐKð'ñ Lõ˜H‘~”~ˆ ؈ ؈ݕs˜8‘}”}¨Ñ)Ñ*Ô*ð 7ð 7ˆAØ × Ò   × 0Ò 0°Ñ 3Ô 3Ñ 4Ô 4Ð 4Ø × !Ò ! ,×"2Ò"2°1Ñ"5Ô"5Ñ 6Ô 6Ð 6Ð 6å*-¨lÑ*;Ô*;ÐE�l 2Ô&Ð&Àˆ Ø8Ð8¨<Ð8Ñ8Ô8ˆØˆ Ý�>Ñ"Ô" QÑ&ˆÝ! .Ñ1Ô1ð "ð "‰HˆC�Øð 4ð 4�Ø��=�=Ø %Ñ'�J�JðIÝ$œN¨1Ñ-Ô-˜˜øÝ$ðIðIðIÝ'ð)AñBôBØGHðIøøøøðIøøøð¥.°Ô"3Ñ3�J�JØ�dŠ{ˆ{ؘcÑ!� øà Ð "Ø ˜$Ñ ˆJØ ð 4ð 4�Ø��=�=Ø %Ñ'�J�JðIÝ$œN¨1Ñ-Ô-˜˜øÝ$ðIðIðIÝ'ð)AñBôBØGHðIøøøøðIøøøð¥.°Ô"3Ñ3�J�Jà ˆzÐИs jÐ0Ð0Ø×#Ò# CÑ(Ô(¨1Ò,ÐL°*×2BÒ2BÀ3Ñ2GÔ2GÈ!Ò2KˆØ ð F�z×'Ò'¨Ñ-Ô-°Ò2Ð2ÝÐDÑEÔEÐ Eð ˆjÐÑØ×!Ò! # rÑ*Ô*×2Ò2°3¸Ñ;Ô;×CÒCÀDÈ"ÑMÔMˆÝÕ(­3¨t©9¬9Ñ4Ñ5Ô5ˆØ—w’w˜v‘”ˆ ؈à �:Ð Ð Ø$.×$4Ò$4°TÑ$:Ô$:Ñ !ˆI�zØ(Ÿš¨sÑ3Ô3Р؈GˆGà)×/Ò/°Ñ4Ô4Р؈Gå!Ð"2Ñ3Ô3ð Ið I‰HˆC�Ø�cˆzˆzØ—I’I˜c‘N”N aÒ'Ð'¨S¯YªY°uÑ-=Ô-=ÀÒ-BÐ-BÝ$Ð%8Ñ9Ô9Ð9ð˜C”=Ô&¨"Ò,Ð,Ø$%�M�Må$'¨°¬ Ô(:¸AÑ$>Ô$>�MØ!¥c¨#¡h¤h°¡lÑ3�Mà  7Ò*Ð*Ø+�Gà  1Ò$Ð$Ý$Ð%EÑFÔFÐFØ" aÒ'Ð'Ø,/¯KªK¸¸rÑ,BÔ,BÐ$ SÑ)Ð)à+¨]¨N¨O¨OÔ<�HØ,/¯KªK¸¸xÑ,HÔ,HÐ$ SÑ)øà—X’XÐ.Ñ/Ô/ˆ Ø �aŠ<ˆ<؈KˆKà&¨ x y yÔ1ˆKà ð ;Ø ˜$ ×!3Ò!3°E¸;Ñ!GÔ!GÑGÑ GˆJˆJð "Ð Ø'×/Ò/°°RÑ8Ô8ˆNÝ�C Ñ/Ô/Ñ0Ô0ð *ð *�Ø�^Ð,Ð,Ý$Ð%JÈQÑ%NÑOÔOÐOØ!×'Ò'¨Ñ*Ô*¨aÒ/Ð/Ø$¨Ñ)Ð$øØŸ'š'¥&­Ð-=Ñ)>Ô)>Ý),¨[Ñ)9Ô)9ñ*:ñ#;ô#;ñ<ô<ˆKð ˜$ Ñ,¨{Ñ:Ñ :ˆJð ˆzÐÐØ-7×-=Ò-=¸dÑ-CÔ-CÑ*ÐÐ*Ð*à%Ðà#×+Ò+¨C°Ñ4Ô4ˆØÐÝ�˜NÑ+Ô+Ñ,Ô,ð &ð &ˆAØ�Ð&Ð&Ý Ð!FÈÑ!JÑKÔKÐKØ×#Ò# AÑ&Ô&¨!Ò+Ð+Ø  AÑ%Ð øð!ð%ð%ˆØ Ð'Ð 'Ð 'ÝÐNØ#ñ$ñ%ô%ð %ð (õ  Ð × !Ò ! #Ñ &Ô &Ñ'Ô'­3¨x©=¬=Ò8Ð8Ýð/ñ0ô0ð 0ð Ð.°Ð 9Ð9s0Å;FÆ F/ÆF*Æ*F/Ç*G?Ç? HÈ HÈH©ÚoptimizeÚ einsum_callcó—|SrMr)r¿rÀr¡s rÚ_einsum_path_dispatcherr¸s €ð €OrÚnumpy)ÚmoduleÚgreedyFc ón ‡9—|}|durd}|€d}d}d}|dust|t¦«rn«t|¦«r|ddkrd}n�t|¦«dkr[t|dt¦«r@t|dttf¦«rt|d¦«}|d}nt d t|¦«z¦«‚|}t |¦«\}}}| d ¦«} d „| D¦«} t|¦«} t|  d d ¦«¦«} iŠ9d „tt| ¦«¦«D¦«} t| ¦«D]à\}}||j }t|¦«t|¦«krtd|||fz¦«‚t|¦«D]„\}}||}|dkr| | |¦«|‰9 ¦«vr;‰9|dkr|‰9|<ŒV|d‰9|fvrtd||‰9||fz¦«‚Œ|‰9|<Œ…Œád„| D¦«} ˆ9fd„| |gzD¦«}t!|¦«}|€|}n|}t#d„| D¦«¦«t| ¦«z dk}t%| |t| ¦«‰9¦«}|r |dd…}nˆ|dust| ¦«dvs| | kr+t'tt| ¦«¦«¦«g}nB|dkrt)| | ‰9|¦«}n)|dkrt+| | ‰9|¦«}nt-d|¦«‚ggggf\}}}}t|¦«D�]þ\}}t't/t1|¦«d¬¦«¦«}t3|| | ¦«}|\}} } }!t%|!| t|¦«‰9¦«}"| |"¦«| t|!¦«¦«| t5|‰9¦«¦«t¦«}#g}$|D]B}%|$ |  |%¦«¦«|#|  |%¦«z}#ŒC|#| z }&t| |#z¦«st9|$|| ¦«}'nd}'|t|¦«z dkr|}(n:ˆ9fd„|D¦«})d  d„t/|)¦«D¦«¦«}(|  |(¦«|  |&¦«d  |$¦«dz|(z}*|| |*| dd…|'f}+| |+¦«�Œt#|¦«dz},t| ¦«dkr2t=d t| ¦«dz ¦«¦«‚|r||fS|dz|z}-d}.||,z }/t!|¦«}0d|-z}1|1dt| ¦«zz }1|1dt!|¦«zz }1|1d |zz }1|1d!|,zz }1|1d"|/zz }1|1d#|0zz }1|1d$z }1|1d%|.zz }1|1d&z }1t|¦«D];\}2}+|+\}3}4}*}5}6d  |5¦«dz|z}7||2|*|7f}8|1d'|8zz }1Œ>> np.random.seed(123) >>> a = np.random.rand(2, 2) >>> b = np.random.rand(2, 5) >>> c = np.random.rand(5, 2) >>> path_info = np.einsum_path('ij,jk,kl->il', a, b, c, optimize='greedy') >>> print(path_info[0]) ['einsum_path', (1, 2), (0, 1)] >>> print(path_info[1]) Complete contraction: ij,jk,kl->il # may vary Naive scaling: 4 Optimized scaling: 3 Naive FLOP count: 1.600e+02 Optimized FLOP count: 5.600e+01 Theoretical speedup: 2.857 Largest intermediate: 4.000e+00 elements ------------------------------------------------------------------------- scaling current remaining ------------------------------------------------------------------------- 3 kl,jk->jl ij,jl->il 3 jl,ij->il il->il A more complex index transformation example. >>> I = np.random.rand(10, 10, 10, 10) >>> C = np.random.rand(10, 10) >>> path_info = np.einsum_path('ea,fb,abcd,gc,hd->efgh', C, C, I, C, C, ... optimize='greedy') >>> print(path_info[0]) ['einsum_path', (0, 2), (0, 3), (0, 2), (0, 1)] >>> print(path_info[1]) Complete contraction: ea,fb,abcd,gc,hd->efgh # may vary Naive scaling: 8 Optimized scaling: 5 Naive FLOP count: 8.000e+08 Optimized FLOP count: 8.000e+05 Theoretical speedup: 1000.000 Largest intermediate: 1.000e+04 elements -------------------------------------------------------------------------- scaling current remaining -------------------------------------------------------------------------- 5 abcd,ea->bcde fb,gc,hd,bcde->efgh 5 bcde,fb->cdef gc,hd,cdef->efgh 5 cdef,gc->defg hd,defg->efgh 5 defg,hd->efgh efgh->efgh TrÅNFrrr.r zDid not understand the path: %srŠcó,—g|]}t|¦«‘ŒSr©r©rOr2s rr‡zeinsum_path..Ts€Ð-Ð-Ð-˜Q•#�a‘&”&Ð-Ð-Ð-rr‚có—g|]}g‘ŒSrrrÉs rr‡zeinsum_path..Zs€Ð<Ð<Ð< ˜Ð<Ð<Ð.ts€Ð;Ð;Ð; A�˜Q™œÐ;Ð;Ð;rcó0•—g|]}t|‰¦«‘ŒSrrN)rOÚtermÚdimension_dicts €rr‡zeinsum_path..ws3ø€ð>ð>ð>Øõ' t¨^Ñ<Ô<ð>ð>ð>rc3ó4K—|]}t|¦«V—ŒdSrM)r8rÉs rrQzeinsum_path..‚s(èè€Ð4Ð4 A�˜Q™œÐ4Ð4Ð4Ð4Ð4Ð4r)r r.ÚoptimalzPath name %s not found)Úreverser\có$•—g|] }‰||f‘Œ Srr)rOr(rÎs €rr‡zeinsum_path..·s#ø€ÐJÐJÐJ¸#˜N¨3Ô/°Ð5ÐJÐJÐJrcó—g|] }|d‘Œ S)r rrÉs rr‡zeinsum_path..¸s€Ð!DÐ!DÐ!D¨1 ! A¤$Ð!DÐ!DÐ!Drr‹zHInvalid einsum_path is specified: {} more operands has to be contracted.)ÚscalingÚcurrentr'z Complete contraction: %s z Naive scaling: %d z Optimized scaling: %d z Naive FLOP count: %.3e z Optimized FLOP count: %.3e z Theoretical speedup: %3.3f z' Largest intermediate: %.3e elements zK-------------------------------------------------------------------------- z%6s %24s %40s zJ--------------------------------------------------------------------------z %4d %24s %40s) r‘r’r8r]Úfloatršr½r�rr“r7r ržr�r!Úkeysr rRrr<rqrJÚKeyErrorr r•r,r r–rrœÚ RuntimeErrorÚformat):r¿rÀr¡Ú path_typeÚexplicit_einsum_pathr=Úeinsum_call_argr»r¸Ú input_listr#r$rÚbroadcast_indicesÚtnumrÍÚshÚcnumr¼ÚdimÚ size_listÚmax_sizeÚ memory_argÚ inner_productrTrIÚ cost_listÚ scale_listÚcontraction_listÚ contract_indsrUÚout_indsr+r%rBÚbcastÚ tmp_inputsr2Únew_bcast_indsÚdo_blasrVÚ sort_resultÚ einsum_strÚ contractionÚopt_costÚoverall_contractionÚheaderÚspeedupÚmax_iÚ path_printÚnÚindsÚidx_rmr'ÚblasÚ remaining_strÚpath_runrÎs: @rrrÂs"ø€ð`€IØ�DÐÐØˆ ØÐ؈ à ÐØ€Lð �UÐÐ�z¨)µSÑ9Ô9ÐØ õ ˆY‰Œð L˜Y qœ\¨]Ò:Ð:Ø#ÐÐõ ˆy‰>Œ>˜QÒ Ð ¥J¨y¸¬|½SÑ$AÔ$AÐ Ý �y ”|¥c­5 \Ñ 2Ô 2ð å˜9 Qœ<Ñ(Ô(ˆ ؘa”Lˆ ˆ õÐ9½CÀ ¹N¼NÑJÑKÔKÐKð"€Oõ4GÀxÑ3PÔ3PÑ0ÐÐ&¨ð"×'Ò'¨Ñ,Ô,€JØ-Ð- *Ð-Ñ-Ô-€JÝÐ%Ñ&Ô&€JÝÐ"×*Ò*¨3°Ñ3Ô3Ñ4Ô4€Gð€NØ<Ð<¥U­3¨z©?¬?Ñ%;Ô%;Ð<Ñ<Ô<ÐÝ  Ñ+Ô+ð+ð+‰ ˆˆdØ �dŒ^Ô !ˆÝ ˆr‰7Œ7•c˜$‘i”iÒ Ð ÝðIà 0°Ô 6¸Ð=ñ>ñ?ô?ð ?õ$ D™/œ/ð +ð +‰JˆD�$Ø�T”(ˆCð�aŠxˆxØ! $Ô'×.Ò.¨tÑ4Ô4Ð4à�~×*Ò*Ñ,Ô,Ð,Ð,à! $Ô'¨1Ò,Ð,Ø+.�N 4Ñ(Ð(Ø  N°4Ô$8Ð 9Ð9Ð9Ý$ð&Kà(,¨d°NÀ4Ô4HÈ#Ð'Nñ&OñPôPðPð:ð (+�˜tÑ$Ð$ð! +ð&<Ð;Ð):Ð;Ñ;Ô;Ðð>ð>ð>ð>Ø'Ð+;Ð*<Ñ<ð>ñ>ô>€Iå�9‰~Œ~€HàÐØˆ ˆ à!ˆ õÐ4Ð4¨Ð4Ñ4Ô4Ñ4Ô4µs¸7±|´|ÑCÀqÒH€MݘW mµS¸±_´_ÀnÑUÔU€Jðð<ؘ˜˜Œ}ˆˆà �eÐ Ð Ý � ‰OŒO˜vÐ %Ð %Ø �zÒ !Ð !õ•e�C  ™OœOÑ,Ô,Ñ-Ô-Ð.ˆˆØ �hÒ Ð Ý˜J¨ °NÀJÑOÔOˆˆØ �iÒ Ð Ý˜Z¨°^ÀZÑPÔPˆˆåÐ/°Ñ;Ô;Ð;à9;¸RÀÀR¸Ñ6€Iˆz˜9Ð&6õ )¨™œð&-ñ&-шˆmå�f¥T¨-Ñ%8Ô%8À$ÐGÑGÔGÑHÔHˆ å$ ]°JÀ ÑKÔKˆØ:BÑ7ˆ�*˜k¨<å˜<¨µc¸-Ñ6HÔ6HÈ.ÑYÔYˆØ×Ò˜ÑÔÐØ×Ò�#˜lÑ+Ô+Ñ,Ô,Ð,Ø×ÒÕ.¨x¸ÑHÔHÑIÔIÐIå‘”ˆØˆ Øð .ð .ˆAØ × Ò ˜jŸnšn¨QÑ/Ô/Ñ 0Ô 0Ð 0Ø Ð&×*Ò*¨1Ñ-Ô-Ñ -ˆEˆEà Ñ,ˆõ�; Ñ&Ñ'Ô'ð ݘz¨8°[ÑAÔAˆGˆGàˆGð •3�t‘9”9Ñ  Ò #Ð #Ø)ˆJˆJàJÐJÐJÐJÀÐJÑJÔJˆKØŸšÐ!DÐ!Dµ°{Ñ0CÔ0CÐ!DÑ!DÔ!DÑEÔEˆJà×Ò˜*Ñ%Ô%Ð%Ø× Ò  Ñ0Ô0Ð0Ø—X’X˜jÑ)Ô)¨DÑ0°:Ñ=ˆ à$ k°:¸zÈ!È!È!¼}ÈgÐVˆ Ø×Ò  Ñ,Ô,Ð,Ñ,å�9‰~Œ~ Ñ!€Hå ˆ:�„˜!ÒÐõð ß š&¥ Z¡¤°1Ñ!4Ñ5Ô5ñ7ô7ð 7ðð,ØÐ*Ð+Ð+ð+¨TÑ1Ð4DÑDÐØ 0€Fà˜8Ñ#€GÝ � ‰NŒN€Eà1Ð4GÑG€JØÐ1µC¸±L´LÑ@Ñ@€JØÐ1µC¸ ±O´OÑCÑC€JØÐ3°jÑ@Ñ@€JØÐ3°hÑ>Ñ>€JØÐ4°wÑ>Ñ>€JØÐ<¸uÑDÑD€JØ�/Ñ!€JØÐ# fÑ,Ñ,€JØ�(Ñ€Jå#Ð$4Ñ5Ô5ð6ð6‰ˆˆ;Ø4?Ñ1ˆˆf�j )¨TØŸš Ñ+Ô+¨dÑ2Ð5EÑEˆ ؘq”M :¨}Ð=ˆØÐ*¨XÑ5Ñ5ˆ ˆ à ˆ?˜TÑ !€DØ �*Ð Ðr)Úoutr¿c/ó"K—|Ed{V—†|V—dSrMr)rr¿r¡Úkwargss rÚ_einsum_dispatcherrés-èè€ðÐÐÐÐÐÐÐØ €I€I€I€I€Irc 󤇇—|du}|dur|r||d<t‰i|¤ŽSgd¢Šˆfd„| ¦«D¦«}t|¦«rtd|z¦«‚t ‰|ddœŽ\Š}| d d ¦«}| ¦«d krtd „‰D¦«¦«rd }nd}t|¦«D�]g\}} | \} } } } }ˆfd„| D¦«}|o|dzt|¦«k}|�r|   d¦«\}}|  d¦«\}}||z}| D]}|  |d¦«}Œgg}}t| ¦«D]R}|  |  |¦«¦«|  |  |¦«¦«ŒSt|dt|¦«t|¦«fiŽ}||ks|r|r||d<t|dz|z|fi|¤Ž}n|r||d<t| g|¢Ri|¤Ž}‰  |¦«~~�Œi|r|St!‰d|¬¦«S)af5 einsum(subscripts, *operands, out=None, dtype=None, order='K', casting='safe', optimize=False) Evaluates the Einstein summation convention on the operands. Using the Einstein summation convention, many common multi-dimensional, linear algebraic array operations can be represented in a simple fashion. In *implicit* mode `einsum` computes these values. In *explicit* mode, `einsum` provides further flexibility to compute other array operations that might not be considered classical Einstein summation operations, by disabling, or forcing summation over specified subscript labels. See the notes and examples for clarification. Parameters ---------- subscripts : str Specifies the subscripts for summation as comma separated list of subscript labels. An implicit (classical Einstein summation) calculation is performed unless the explicit indicator '->' is included as well as subscript labels of the precise output form. operands : list of array_like These are the arrays for the operation. out : ndarray, optional If provided, the calculation is done into this array. dtype : {data-type, None}, optional If provided, forces the calculation to use the data type specified. Note that you may have to also give a more liberal `casting` parameter to allow the conversions. Default is None. order : {'C', 'F', 'A', 'K'}, optional Controls the memory layout of the output. 'C' means it should be C contiguous. 'F' means it should be Fortran contiguous, 'A' means it should be 'F' if the inputs are all 'F', 'C' otherwise. 'K' means it should be as close to the layout as the inputs as is possible, including arbitrarily permuted axes. Default is 'K'. casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional Controls what kind of data casting may occur. Setting this to 'unsafe' is not recommended, as it can adversely affect accumulations. * 'no' means the data types should not be cast at all. * 'equiv' means only byte-order changes are allowed. * 'safe' means only casts which can preserve values are allowed. * 'same_kind' means only safe casts or casts within a kind, like float64 to float32, are allowed. * 'unsafe' means any data conversions may be done. Default is 'safe'. optimize : {False, True, 'greedy', 'optimal'}, optional Controls if intermediate optimization should occur. No optimization will occur if False and True will default to the 'greedy' algorithm. Also accepts an explicit contraction list from the ``np.einsum_path`` function. See ``np.einsum_path`` for more details. Defaults to False. Returns ------- output : ndarray The calculation based on the Einstein summation convention. See Also -------- einsum_path, dot, inner, outer, tensordot, linalg.multi_dot einops : similar verbose interface is provided by `einops `_ package to cover additional operations: transpose, reshape/flatten, repeat/tile, squeeze/unsqueeze and reductions. opt_einsum : `opt_einsum `_ optimizes contraction order for einsum-like expressions in backend-agnostic manner. Notes ----- .. versionadded:: 1.6.0 The Einstein summation convention can be used to compute many multi-dimensional, linear algebraic array operations. `einsum` provides a succinct way of representing these. A non-exhaustive list of these operations, which can be computed by `einsum`, is shown below along with examples: * Trace of an array, :py:func:`numpy.trace`. * Return a diagonal, :py:func:`numpy.diag`. * Array axis summations, :py:func:`numpy.sum`. * Transpositions and permutations, :py:func:`numpy.transpose`. * Matrix multiplication and dot product, :py:func:`numpy.matmul` :py:func:`numpy.dot`. * Vector inner and outer products, :py:func:`numpy.inner` :py:func:`numpy.outer`. * Broadcasting, element-wise and scalar multiplication, :py:func:`numpy.multiply`. * Tensor contractions, :py:func:`numpy.tensordot`. * Chained array operations, in efficient calculation order, :py:func:`numpy.einsum_path`. The subscripts string is a comma-separated list of subscript labels, where each label refers to a dimension of the corresponding operand. Whenever a label is repeated it is summed, so ``np.einsum('i,i', a, b)`` is equivalent to :py:func:`np.inner(a,b) `. If a label appears only once, it is not summed, so ``np.einsum('i', a)`` produces a view of ``a`` with no changes. A further example ``np.einsum('ij,jk', a, b)`` describes traditional matrix multiplication and is equivalent to :py:func:`np.matmul(a,b) `. Repeated subscript labels in one operand take the diagonal. For example, ``np.einsum('ii', a)`` is equivalent to :py:func:`np.trace(a) `. In *implicit mode*, the chosen subscripts are important since the axes of the output are reordered alphabetically. This means that ``np.einsum('ij', a)`` doesn't affect a 2D array, while ``np.einsum('ji', a)`` takes its transpose. Additionally, ``np.einsum('ij,jk', a, b)`` returns a matrix multiplication, while, ``np.einsum('ij,jh', a, b)`` returns the transpose of the multiplication since subscript 'h' precedes subscript 'i'. In *explicit mode* the output can be directly controlled by specifying output subscript labels. This requires the identifier '->' as well as the list of output subscript labels. This feature increases the flexibility of the function since summing can be disabled or forced when required. The call ``np.einsum('i->', a)`` is like :py:func:`np.sum(a, axis=-1) `, and ``np.einsum('ii->i', a)`` is like :py:func:`np.diag(a) `. The difference is that `einsum` does not allow broadcasting by default. Additionally ``np.einsum('ij,jh->ih', a, b)`` directly specifies the order of the output subscript labels and therefore returns matrix multiplication, unlike the example above in implicit mode. To enable and control broadcasting, use an ellipsis. Default NumPy-style broadcasting is done by adding an ellipsis to the left of each term, like ``np.einsum('...ii->...i', a)``. To take the trace along the first and last axes, you can do ``np.einsum('i...i', a)``, or to do a matrix-matrix product with the left-most indices instead of rightmost, one can do ``np.einsum('ij...,jk...->ik...', a, b)``. When there is only one operand, no axes are summed, and no output parameter is provided, a view into the operand is returned instead of a new array. Thus, taking the diagonal as ``np.einsum('ii->i', a)`` produces a view (changed in version 1.10.0). `einsum` also provides an alternative way to provide the subscripts and operands as ``einsum(op0, sublist0, op1, sublist1, ..., [sublistout])``. If the output shape is not provided in this format `einsum` will be calculated in implicit mode, otherwise it will be performed explicitly. The examples below have corresponding `einsum` calls with the two parameter methods. .. versionadded:: 1.10.0 Views returned from einsum are now writeable whenever the input array is writeable. For example, ``np.einsum('ijk...->kji...', a)`` will now have the same effect as :py:func:`np.swapaxes(a, 0, 2) ` and ``np.einsum('ii->i', a)`` will return a writeable view of the diagonal of a 2D array. .. versionadded:: 1.12.0 Added the ``optimize`` argument which will optimize the contraction order of an einsum expression. For a contraction with three or more operands this can greatly increase the computational efficiency at the cost of a larger memory footprint during computation. Typically a 'greedy' algorithm is applied which empirical tests have shown returns the optimal path in the majority of cases. In some cases 'optimal' will return the superlative path through a more expensive, exhaustive search. For iterative calculations it may be advisable to calculate the optimal path once and reuse that path by supplying it as an argument. An example is given below. See :py:func:`numpy.einsum_path` for more details. Examples -------- >>> a = np.arange(25).reshape(5,5) >>> b = np.arange(5) >>> c = np.arange(6).reshape(2,3) Trace of a matrix: >>> np.einsum('ii', a) 60 >>> np.einsum(a, [0,0]) 60 >>> np.trace(a) 60 Extract the diagonal (requires explicit form): >>> np.einsum('ii->i', a) array([ 0, 6, 12, 18, 24]) >>> np.einsum(a, [0,0], [0]) array([ 0, 6, 12, 18, 24]) >>> np.diag(a) array([ 0, 6, 12, 18, 24]) Sum over an axis (requires explicit form): >>> np.einsum('ij->i', a) array([ 10, 35, 60, 85, 110]) >>> np.einsum(a, [0,1], [0]) array([ 10, 35, 60, 85, 110]) >>> np.sum(a, axis=1) array([ 10, 35, 60, 85, 110]) For higher dimensional arrays summing a single axis can be done with ellipsis: >>> np.einsum('...j->...', a) array([ 10, 35, 60, 85, 110]) >>> np.einsum(a, [Ellipsis,1], [Ellipsis]) array([ 10, 35, 60, 85, 110]) Compute a matrix transpose, or reorder any number of axes: >>> np.einsum('ji', c) array([[0, 3], [1, 4], [2, 5]]) >>> np.einsum('ij->ji', c) array([[0, 3], [1, 4], [2, 5]]) >>> np.einsum(c, [1,0]) array([[0, 3], [1, 4], [2, 5]]) >>> np.transpose(c) array([[0, 3], [1, 4], [2, 5]]) Vector inner products: >>> np.einsum('i,i', b, b) 30 >>> np.einsum(b, [0], b, [0]) 30 >>> np.inner(b,b) 30 Matrix vector multiplication: >>> np.einsum('ij,j', a, b) array([ 30, 80, 130, 180, 230]) >>> np.einsum(a, [0,1], b, [1]) array([ 30, 80, 130, 180, 230]) >>> np.dot(a, b) array([ 30, 80, 130, 180, 230]) >>> np.einsum('...j,j', a, b) array([ 30, 80, 130, 180, 230]) Broadcasting and scalar multiplication: >>> np.einsum('..., ...', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(',ij', 3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.einsum(3, [Ellipsis], c, [Ellipsis]) array([[ 0, 3, 6], [ 9, 12, 15]]) >>> np.multiply(3, c) array([[ 0, 3, 6], [ 9, 12, 15]]) Vector outer product: >>> np.einsum('i,j', np.arange(2)+1, b) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) >>> np.einsum(np.arange(2)+1, [0], b, [1]) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) >>> np.outer(np.arange(2)+1, b) array([[0, 1, 2, 3, 4], [0, 2, 4, 6, 8]]) Tensor contraction: >>> a = np.arange(60.).reshape(3,4,5) >>> b = np.arange(24.).reshape(4,3,2) >>> np.einsum('ijk,jil->kl', a, b) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]]) >>> np.einsum(a, [0,1,2], b, [1,0,3], [2,3]) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]]) >>> np.tensordot(a,b, axes=([1,0],[0,1])) array([[4400., 4730.], [4532., 4874.], [4664., 5018.], [4796., 5162.], [4928., 5306.]]) Writeable returned arrays (since version 1.10.0): >>> a = np.zeros((3, 3)) >>> np.einsum('ii->i', a)[:] = 1 >>> a array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) Example of ellipsis use: >>> a = np.arange(6).reshape((3,2)) >>> b = np.arange(12).reshape((4,3)) >>> np.einsum('ki,jk->ij', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]]) >>> np.einsum('ki,...k->i...', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]]) >>> np.einsum('k...,jk', a, b) array([[10, 28, 46, 64], [13, 40, 67, 94]]) Chained array operations. For more complicated contractions, speed ups might be achieved by repeatedly computing a 'greedy' path or pre-computing the 'optimal' path and repeatedly applying it, using an `einsum_path` insertion (since version 1.12.0). Performance improvements can be particularly significant with larger arrays: >>> a = np.ones(64).reshape(2,4,8) Basic `einsum`: ~1520ms (benchmarked on 3.1GHz Intel i5.) >>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a) Sub-optimal `einsum` (due to repeated path calculation time): ~330ms >>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal') Greedy `einsum` (faster optimal path approximation): ~160ms >>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='greedy') Optimal `einsum` (best usage pattern in some use cases): ~110ms >>> path = np.einsum_path('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize='optimal')[0] >>> for iteration in range(500): ... _ = np.einsum('ijk,ilm,njm,nlk,abc->',a,a,a,a,a, optimize=path) NFr)ÚdtypeÚorderÚcastingcó"•—g|] \}}|‰v¯ |‘Œ Srr)rOÚkr†Úvalid_einsum_kwargss €rr‡zeinsum..`s2ø€ð4ð4ð4™F˜Q ØÐ2Ð2Ð2ðØ2Ð2Ð2rz+Did not understand the following kwargs: %sTr¾rÚKÚAc3ó.K—|]}|jjV—ŒdSrM)ÚflagsÚ f_contiguous)rOÚarrs rrQzeinsum..ms'èè€Ð:Ð:¨#ˆsŒyÔ%Ð:Ð:Ð:Ð:Ð:Ð:rÚFÚCcó:•—g|]}‰ |¦«‘ŒSr)r–)rOr2r¡s €rr‡zeinsum..us#ø€Ð6Ð6Ð6¨A˜Ÿ š  Q™œÐ6Ð6Ð6rr r‹rŠr‚Úaxesr)r)rÚitemsr8ršrr–ÚupperÚallr r�r“r r!Úfindrr<r)rr¿r¡rÚ specified_outÚunknown_kwargsrêÚ output_orderr©rórûrüròr'rýr¤Ú handle_outÚ input_strÚ results_indexrurvÚ tensor_resultr£Úleft_posÚ right_posÚnew_viewr s ` @rrrñs5øø€ðH ˜t�O€Mð�5ÐÐØ ð ØˆF�5‰MݘÐ, VÐ,Ð,Ð,ð8Ð7Ð7Ðð4ð4ð4ð4 f§l¢l¡n¤nð4ñ4ô4€Nå ˆ>ÑÔð*ÝÐEØ(ñ)ñ*ô*ð *õ"-¨hÀØ9=ð"?ð"?ð"?Ñ€HÐð—:’:˜g sÑ+Ô+€LØ×ÒÑÔ˜sÒ"Ð"Ý Ð:Ð:°Ð:Ñ:Ô:Ñ :Ô :ð ØˆLˆLàˆLõ&Ð&6Ñ7Ô7ð+#ñ+#шˆ[Ø4?Ñ1ˆˆf�j )¨TØ6Ð6Ð6Ð6°Ð6Ñ6Ô6ˆ ð#ÐK¨¨q©µSÐ9IÑ5JÔ5JÒ(Jˆ ð ñ Eà'1×'7Ò'7¸Ñ'=Ô'=Ñ $ˆI�}Ø&/§o¢o°cÑ&:Ô&:Ñ #ˆJ˜ à&¨Ñ4ˆMØð =ð =�Ø -× 5Ò 5°a¸Ñ <Ô <� � ð#% b�iˆHݘF‘^”^ð 6ð 6�Ø—’  §¢°Ñ 2Ô 2Ñ3Ô3Ð3Ø× Ò  ×!1Ò!1°!Ñ!4Ô!4Ñ5Ô5Ð5Ð5õ! ,ÐYµe¸H±o´oÅuÈYÑGWÔGWÐ5XÐYÐYˆHð Ò.Ð.°:Ð.Øð(Ø$'�F˜5‘MÝ# M°DÑ$8¸=Ñ$HÈ(Ð]Ð]ÐV\Ð]Ð]�øð ð $Ø #��u‘ õ   ÐD¨\ÐDÐDÐD¸VÐDÐDˆHð �Š˜Ñ!Ô!Ð!Ø ˜(™(àð;؈ å˜( 1œ+¨\Ð:Ñ:Ô:Ð:r)Ú__doc__r9r˜Únumpy.core.multiarrayrÚnumpy.core.numericrrÚnumpy.core.overridesrÚ__all__r”rr›rr r,rJrZrhrqrr½rÂrrrrrrúr(sìððððÐÐÐØ€€€à*Ð*Ð*Ð*Ð*Ð*Ø4Ð4Ð4Ð4Ð4Ð4Ð4Ð4Ø8Ð8Ð8Ð8Ð8Ð8à �]Ð #€àG€Ø�S˜Ñ(Ô(Ðð$$ð$$ð$$ðLððð:9>ð9>ð9>ðxDðDðDðL7-ð7-ð7-ðt%ð%ð%ðNbðbðbðJkðkðkð\j:ðj:ðj:ðZ15À$ððððððÐÐ0¸ÐAÑAÔAØ$,¸%ðcðcðcðcñBÔAðcðL '+°TððððððÐÐ+°GÐ<Ñ<Ô<بðq;ðq;ðq;ðq;ñ=Ô<ðq;ðq;ðq;r