K ifdZddlmZddlmZddlmZddlmZddl m Z m Z ddl m Z ddlZddlZdd lmZmZdd lmZdd lmZmZmZmZdd lmZmZmZmZmZm Z dd l!m"Z"ddl#m$Z$m%Z%ddl&m'Z'ddl(m)Z)ddl*m+Z+m,Z,m-Z-ddl.m/Z/m0Z0ddl1m2Z2ddl3m4Z4ddl5m6Z6ddl7m8Z8m9Z9m:Z:ddl;mm?Z?dZ@dZAdZBGdde/ZCdZDGdd e/ZEeEZFGd!d"ZGeGZHGd#d$eZIGd%d&eZJd'ZKGd(d)eZLe=d*d+d,-d.ZMe=d/d+d0-Gd1d2eZNe=d3d+d4-d[d5ZOGd6d7eZPd[d8ZQGd9d:ee ZRGd;deRZTGd?d@eRe2ZUGdAdBeRZVGdCdDePZWGdEdFeTZXGdGdHeJZYGdIdJeZZdKZ[dLZ\dMZ]dNZ^dOZ_dPZ`dQZadRZbdSZcdTZddUZed\dVZfdWZgdXZhdYZidZeiegiejeR<y)]a This module defines tensors with abstract index notation. The abstract index notation has been first formalized by Penrose. Tensor indices are formal objects, with a tensor type; there is no notion of index range, it is only possible to assign the dimension, used to trace the Kronecker delta; the dimension can be a Symbol. The Einstein summation convention is used. The covariant indices are indicated with a minus sign in front of the index. For instance the tensor ``t = p(a)*A(b,c)*q(-c)`` has the index ``c`` contracted. A tensor expression ``t`` can be called; called with its indices in sorted order it is equal to itself: in the above example ``t(a, b) == t``; one can call ``t`` with different indices; ``t(c, d) == p(c)*A(d,a)*q(-a)``. The contracted indices are dummy indices, internally they have no name, the indices being represented by a graph-like structure. Tensors are put in canonical form using ``canon_bp``, which uses the Butler-Portugal algorithm for canonicalization using the monoterm symmetries of the tensors. If there is a (anti)symmetric metric, the indices can be raised and lowered when the tensor is put in canonical form. ) annotations)Any)reduce)prod)abstractmethodABC) defaultdictN)IntegerRational Permutation)get_symmetric_group_sgsbsgs_direct_product canonicalize riemann_bsgs)BasicExprsympifyAddMulS) clear_cache)TupleDict) WildFunction)default_sort_key)SymbolsymbolsWild) CantSympify_sympify)AssocOp) SYMPY_INTS)eye)sympy_deprecation_warningSymPyDeprecationWarningignore_warnings)memoize_property deprecated)siftc"tddddy)Nz| The data attribute of TensorIndexType is deprecated. Use The replace_with_arrays() method instead. z1.4z deprecated-tensorindextype-attrsdeprecated_since_versionactive_deprecations_target stacklevelr%Y/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/tensor/tensor.pydeprecate_datar5?s "'#Er3c"tddddy)Nzn The Tensor.fun_eval() method is deprecated. Use Tensor.substitute_indices() instead. 1.5deprecated-tensor-fun-evalr,r-r1r2r3r4deprecate_fun_evalr9J "'#?r3c"tddddy)Nzx Calling a tensor like Tensor(*indices) is deprecated. Use Tensor.substitute_indices() instead. r7r8r,r-r1r2r3r4deprecate_callr<Vr:r3ceZdZdZddZedZedZedZdZ edZ edZ ed Z dd Z d Zd Zd ZdZdZdZddZdZy)_IndexStructurea This class handles the indices (free and dummy ones). It contains the algorithms to manage the dummy indices replacements and contractions of free indices under multiplications of tensor expressions, as well as stuff related to canonicalization sorting, getting the permutation of the expression and so on. It also includes tools to get the ``TensorIndex`` objects corresponding to the given index structure. c||_||_||_||_t |jdt |jzz|_|jj dy)Nc |dSNrr2xs r4z*_IndexStructure.__init__..rs AaDr3key)freedum index_typesindiceslen _ext_ranksort)selfrHrIrJrKcanon_bps r4__init__z_IndexStructure.__init__lsQ & TYY!CM/9  . )r3ctj|\}}|Dcgc]}|j}}tj|||}t||||Scc}w)a Create a new ``_IndexStructure`` object from a list of ``indices``. Explanation =========== ``indices`` ``TensorIndex`` objects, the indices. Contractions are detected upon construction. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, _IndexStructure >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> _IndexStructure.from_indices(m0, m1, -m1, m3) _IndexStructure([(m0, 0), (m3, 3)], [(1, 2)], [Lorentz, Lorentz, Lorentz, Lorentz]) )r>_free_dum_from_indicestensor_index_type_replace_dummy_names)rKrHrIirJs r4 from_indicesz_IndexStructure.from_indicests[*$::GD c4;*generate_indices_from_free_dum_index_types) componentsrHrIrJ componentrKs r4from_components_free_dumz(_IndexStructure.from_components_free_dumsQ # 6I   y44 5 6!LLTSVXcdtS+w??r3cZt|}|dk(r |ddfggfSdgt|z}i}g}t|D]\}}|j}|j}|j} ||f|vrp|||f\} } | r| rt d| |fzd|| <d||<n| r d|| <d||<nt d| |fz| r|j || f|j | |f|j|f|||f<t|Dcgc]\}}||s ||f}}}|j||fScc}}w)a Convert ``indices`` into ``free``, ``dum`` for single component tensor. Explanation =========== ``free`` list of tuples ``(index, pos, 0)``, where ``pos`` is the position of index in the list of indices formed by the component tensors ``dum`` list of tuples ``(pos_contr, pos_cov, 0, 0)`` Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, _IndexStructure >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2, m3 = tensor_indices('m0,m1,m2,m3', Lorentz) >>> _IndexStructure._free_dum_from_indices(m0, m1, -m1, m3) ([(m0, 0), (m3, 3)], [(1, 2)]) rTz2two equal contravariant indices in slots %d and %dFz.two equal covariant indices in slots %d and %d)rL enumeratenamerTis_up ValueErrorappendrN) rKnrH index_dictrIrVindexrbtypcontris_contrposs r4rSz&_IndexStructure._free_dum_from_indicessp0 L 6QZO$b( (vc'l" !'* 9HAu::D))CKKEc{j( *D#; 7 #()]`cef_g)ghh$)S "'Q$)S "'Q()Y\_ab[c)cddJJ3x(JJQx(*/++q. D#;'3 96,5W+=Ixq%a II SyJs < D' D'c |jddS)z_ Get a list of indices, creating new tensor indices to complete dummy indices. N)rKrOs r4 get_indicesz_IndexStructure.get_indicess||Ar3c"dgt|dt|zzz}|D] \}}|||< tj|}|D]2\}}||} || } t| | d||<t| | d||<4tj |||S)Nr@TF)rLr> _get_generator_for_dummy_indices TensorIndexrU) rHrIrJrKidxrlgenerate_dummy_namepos1pos2typ1indnames r4r[z:_IndexStructure.generate_indices_from_free_dum_index_typess&#d)Ac#hJ./ HCGCL .NNtT >JD$t$D)$/G't 33GT3GGr3c Ltt|D]\}}|jjdd|jj k(s.dummy_name_gen:S*+,B ! "a ' "$//#5: :r3)r intrbsplitrTrmax)rHindxiposrrs @r4rqz0_IndexStructure._get_generator_for_dummy_indicess# qJD$yys#A&$*@*@*K*KK.1#d6L6L2MsSWS\S\SbSbcfSghiSjOknoOo.pD**+ q ; r3c>|jdt|}t|t|dt|zzk(sJtj |}|D]<\}}||j }||}t ||d||<t ||d||<>|S)Nc |dSrBr2rCs r4rEz6_IndexStructure._replace_dummy_names..s qtr3rFr@TF)rNlistrLr>rqrTrr) rKrHrI new_indicesrtipos1ipos2rwrxs r4rUz$_IndexStructure._replace_dummy_namess ^$7m 7|s4y1SX:5555-NNtT CLE5u%77D)$/G!,WdD!AK !,WdE!BK   C r3cbt|jd}|Dcgc]}|d c}Scc}w)z- Get a list of free indices. c |dSNr`r2rCs r4rEz2_IndexStructure.get_free_indices.. s qtr3rFrsortedrH)rOrHrVs r4get_free_indicesz _IndexStructure.get_free_indicess, dii^4"#!###s ,cddj|j|j|jS)Nz_IndexStructure({}, {}, {}))formatrHrIrJrns r4__str__z_IndexStructure.__str__s%,33DIItxxIYIYZZr3c"|jSrY)rrns r4__repr__z_IndexStructure.__repr__s||~r3cJ|jdd}|jd|S)Nc |dSrBr2rCs r4rEzD_IndexStructure._get_sorted_free_indices_for_canon..s qtr3rF)rHrN)rO sorted_frees r4"_get_sorted_free_indices_for_canonz2_IndexStructure._get_sorted_free_indices_for_canons&iil ^,r3c2t|jdS)Nc |dSrBr2rCs r4rEzC_IndexStructure._get_sorted_dum_indices_for_canon..s adr3rF)rrIrns r4!_get_sorted_dum_indices_for_canonz1_IndexStructure._get_sorted_dum_indices_for_canonsdhhN33r3c|jd}dg|jz}t|jD]\}}||||<|SrB)indices_canon_argsrMrarJ)rO permutationrJrVits r4)_get_lexicographically_sorted_index_typesz9_IndexStructure._get_lexicographically_sorted_index_typessW--/2 fT^^+ t//0 -EAr*,K A ' -r3c|jd}dg|jz}t|jD]\}}||||<|SrB)rrMrarK)rOrrKrVrs r4%_get_lexicographically_sorted_indicesz5_IndexStructure._get_lexicographically_sorted_indices%sT--/2 &'t||, )EAr&(GKN # )r3c||jDcgc]}|d }}|j}|j}t|}|j}t ||z dzDcgc]}dgdz } }g} dg|z} dg|z} t |D]s}||} || | |<|| | |<| |kr0|| }| ||| j k(sJ| j||fM| |z }t|d\}}|r || |d<l|| |d<u| Dcgc] }t|} }t| | | | Scc}wcc}wcc}w)at Returns a ``_IndexStructure`` instance corresponding to the permutation ``g``. Explanation =========== ``g`` permutation corresponding to the tensor in the representation used in canonicalization ``is_canon_bp`` if True, then ``g`` is the permutation corresponding to the canonical form of the tensor rr@Nr`) rrrrLrMrangerTredivmodtupler>)rOg is_canon_bprVrlex_index_types lex_indicesnfreerankrIrHrJrKgiindjidumcovrDs r4 perm2tensorz_IndexStructure.perm2tensor,sw&*%L%L%NOqtO OHHJ@@B K ~~!&u q'8!9:Avax::fTk &+t %A1B,R0KN$RGAJEz!"o"1~R)J)JJJJ S!H%J"1aL c#$CIaL#$CIaL %"%%AuQx%%tS+w??7P ;(&s D/+ D4 D9chddlm}|j}dg|z||dzgz}d}t|j D] \}\}}|||<t |j }t |j } g} d} g} g} |jD]\}}| ||<| dz||<| dz } |j|}|| k7r>| r| j| ||dzg} |} | j||jn| j||dzg|dz }| r| j| ||| | fS)z Returns ``(g, dummies, msym, v)``, the entries of ``canonicalize`` See ``canonicalize`` in ``tensor_can.py`` in combinatorics module. r)_af_newNr`c|y|j}|tjdk(ry|tjdk(ryy)Nr@rr`)symmetryTensorSymmetryfully_symmetric)metricsyms r4metric_symmetry_to_msymzC_IndexStructure.indices_canon_args..metric_symmetry_to_msymcsB~//Cn44Q77n44R88r3r@) sympy.combinatorics.permutationsrrMrarrLrHrrJrerrZ)rOrrfrrrVrrrlrdummiesprevamsymrrris r4rz"_IndexStructure.indices_canon_argsVsS = NN F1H1Q3x   ))P)P)RS OA|dAdG $))n  N  BBD LE5AeH1uAeH FA""5)Cd{NN1%#'N 3CJJ?@#sQw( 1HC  NN1 qz7D((r3NFreturnzlist[TensorIndex])__name__ __module__ __qualname____doc__rQ staticmethodrWr^rSror[rqrUrrrrrrrrrr2r3r4r>r>bs*@@2@@<<|  H H   $[ 4(@T4)r3r>cg}d}|D]-}||k(r|ddxxdz cc<|}|j|dg/g}|D]\}}|jdvr |j}n*tj|j|j}|j|jj |jj ||f|S)Nr`rr`)recomm TensorManagerget_commrbase generators)r\numtyprtvhrfrs r4components_canon_argsrs F D % 9 2JqMQ MD MM4) $ % AD1 66V 66D ))!&&!&&9D !**//1::#8#8!TBC D Hr3ceZdZUdZiZded<iZded<dZdZe dZ dZ d Z d Z d Zd Zd ZdZdZe dZe dZe dZe ddZe dZe dZe dZy)_TensorDataLazyEvaluatora EXPERIMENTAL: do not rely on this class, it may change without deprecation warnings in future versions of SymPy. Explanation =========== This object contains the logic to associate components data to a tensor expression. Components data are set via the ``.data`` property of tensor expressions, is stored inside this class as a mapping between the tensor expression and the ``ndarray``. Computations are executed lazily: whereas the tensor expressions can have contractions, tensor products, and additions, components data are not computed until they are accessed by reading the ``.data`` property associated to the tensor expression. zdict[Any, Any]_substitutions_dict_substitutions_dict_tensmulc|j|}|yddlm}t||s|S|j dk(r|dS|j dk(rt |dk(r|dS|S)Nr`) NDimArrayrr2)_getarrayr isinstancerrL)rOrGdatrs r4 __getitem__z$_TensorDataLazyEvaluator.__getitem__sdiin ;$#y)J 88:?r7N XXZ1_SQq6M r3c ||jvr|j|St|tryt|trt |j Dcgc]}|j c}}|jf|z}||jvr|j|S|j|g}|j||j|jSt|trm|j}t|dk(rt|dj dk(rgt |dj Dcgc]}|j c}}|dj df|z}||jvr|j|S|Dcgc]@}t|t"st|tr|j|n |j$B}}t'|Dcgc]}t|t"r|c}}t)d|Dryt+d|Dr t-d|j||j|j} || zSt|t.rZg}g} |jD]|} t| t"rH|j1| j$| j1| j2D cgc]} | d c} [|j1| | j1g~t)d|Dryt+d|Dr t-dg} dd lm}t9|| D]w\}}t|d kr| j1|&t;|D cic]\} }||  }} }|j<D cgc]} ||  }} | j1|||yt?d | Sycc}wcc}wcc}wcc}wcc} wcc}} wcc} w) a] Retrieve ``data`` associated with ``key``. Explanation =========== This algorithm looks into ``self._substitutions_dict`` for all ``TensorHead`` in the ``TensExpr`` (or just ``TensorHead`` if key is a TensorHead instance). It reconstructs the components data that the tensor expression should have by performing on components data the operations that correspond to the abstract tensor operations applied. Metric tensor is handled in a different manner: it is pre-computed in ``self._substitutions_dict_tensmul``. Nr`rc3$K|]}|du ywrYr2.0rVs r4 z0_TensorDataLazyEvaluator._get..0190c3$K|]}|du ywrYr2rs r4rz0_TensorDataLazyEvaluator._get..rrzSMixing tensors with associated components data with tensors without components datac3$K|]}|du ywrYr2rs r4rz0_TensorDataLazyEvaluator._get..rrc3$K|]}|du ywrYr2rs r4rz0_TensorDataLazyEvaluator._get..rr permutedimsr@c ||zSrYr2)rDys r4rEz/_TensorDataLazyEvaluator._get..s qsr3) rr TensorHeadTensorrrorcr]rdata_from_tensordata_contract_dumrIext_rankTensMulargsrLr\TensExprdatarallanyrdTensAddrerHrrzipra free_argsr)rOrGrV signaturesrch array_list tensmul_args data_listcoeff data_resultfree_args_listargrDsum_listrrrr free_args_posaxess r4rz_TensorDataLazyEvaluator._gets $** *++C0 0 c: & c6 "0AB1qwwBCIMM#i/Dt77777==//45J))*cggs||L L c7 #88L< A%#l1o.H.H*IQ*N"LO4O4O4Q"Rq177"RS $Q22157)C4;;;;;DAA`lI[\pz{|GqHZ65J..q1PQPVPVVIII\QAx9P!QRE0i000i00 "MNN00CGGS\\RK$ $ c7 #INxx .c8,$$SXX."))*BA1Q4*BC$$S)"))"-  .0i000i00 "MNNH *#&y.#A =iy>A%OOD)6? 6J$KdaQT$KM$K:=--H3M#.HDHOOKd$;<  =*H5 5oC#S IQ+C %LHs6O6OO /O O-O< O , O Oc\ddlm}m}m}t t ||}||}||g|S)Nr`) tensorproducttensorcontractionMutableDenseNDimArray)rrrrrmap) ndarray_listrIrrrrarraysprodarrs r4rz*_TensorDataLazyEvaluator.data_contract_dums4RRc/>?( /3//r3cv|y|j||j|j|jdS)z This method is used when assigning components data to a ``TensMul`` object, it converts components data to a fully contravariant ndarray, which is then stored according to the ``TensorHead`` key. NT)_correct_signature_from_indicesrorHrI)rOrtensmul tensorheads r4data_tensorhead_from_tensmulz5_TensorDataLazyEvaluator.data_tensorhead_from_tensmuls? <33     ! LL KK   r3c|j}|jy|j|j|j|j|j S)z This method corrects the components data to the right signature (covariant/contravariant) using the metric associated with each ``TensorIndexType``. N)r]rrrorHrI)rOtensorrs r4rz)_TensorDataLazyEvaluator.data_from_tensor.sP %% ?? "33 OO    KK JJ  r3ct|tr tdt|jdk7r td|jd}|j |||}||fS)Nzcannot assign data to TensAddr`z6cannot assign data to TensMul with multiple componentsr)rrrdrLr\r)rOrGrrnewdatas r4_assign_data_to_tensor_exprz4_TensorDataLazyEvaluator._assign_data_to_tensor_expr?sb c7 #<= = s~~ ! #UV V^^A& 33D#zJ7""r3c ddlm}ddlm}t |t r#|j }|jj}nt |tr0|j }|jdjj}nFt |tr6|jj }|jjj}D]}||k(rdnd}|} |} tt|t|} t|j!dz D]/} || | } t#|| || zz r t%d| } 1y)Nr`r)Flattenrrz'Component data symmetry structure error)rr array.arrayopr#rrrrrrr\TensorIndexTyperrrrorderrrd) rOtensrrr#rrgener sign_change data_swapped last_data permute_axesrVs r4_check_permutations_on_dataz4_TensorDataLazyEvaluator._check_permutations_on_dataIs&* dJ '99D11J f %99D+44??J o .;;##D--88J  )E!&t!4"2KLIE5; 78L5;;=?+ )*<F wy;|+CCDE$%NOO(  ) )r3ctj|}|j||t|tt fs|j ||\}}t|trt|j|jD]\\}}|jtdj||jjsC||jk7sStd||j|<y)aS Set the components data of a tensor object/expression. Explanation =========== Components data are transformed to the all-contravariant form and stored with the corresponding ``TensorHead`` object. If a ``TensorHead`` object cannot be uniquely identified, it will raise an error. NzMindex type {} has no components data associated (needed to raise/lower index)zwrong dimension of ndarray)r parse_datar-rrr%r!rshaperJrrdrdim is_numberr)rOrGvaluerr1 indextypes r4 __setitem__z$_TensorDataLazyEvaluator.__setitem__hs(2259 ((d3 # O<=88dCIC c: &"%djj#//"B CY>>)$&@@Fy@QSS }}..)--'$%ABB C)-  %r3c|j|=yrYrrOrGs r4 __delitem__z$_TensorDataLazyEvaluator.__delitem__s  $ $S )r3c||jvSrYr7r8s r4 __contains__z%_TensorDataLazyEvaluator.__contains__sd....r3c||j|ddf<|j|}||j|ddf<|j}|j}||z|j|ddf<||z|j|ddf<y)a Assign data to the ``metric`` tensor. The metric tensor behaves in an anomalous way when raising and lowering indices. Explanation =========== A fully covariant metric is the inverse transpose of the fully contravariant metric (it is meant matrix inverse). If the metric is symmetric, the transpose is not necessary and mixed covariant/contravariant metrics are Kronecker deltas. TFN)rinverse_transpose_matrixtomatrix)rOrrinverse_transposeminvts r4add_metric_dataz(_TensorDataLazyEvaluator.add_metric_datas"@D((t);< 99$?BS(()=> MMO ))+@AD((u)<=@Dq(()<=r3cddlm}m}|j}|j}|dk(r||||d||zf}|S||||||f}|S)Nr`rrr)rrrr)rrrlrrmdimddims r4_flip_index_by_metricz._TensorDataLazyEvaluator._flip_index_by_metricsx;{{}yy{ !8$DH D %d D r3ch|jj}tj|SrY)r>invrr/ndarrayr@s r4inverse_matrixz'_TensorDataLazyEvaluator.inverse_matrixs*     " " $'22155r3c||jjj}tj |SrY)r>rITrr/rJs r4r=z1_TensorDataLazyEvaluator.inverse_transpose_matrixs0     " " $ & &'22155r3c<t|D]\}}|js.|s,tj||jj |}@|jrM|sPtj|tj |jj |}|S)z Utility function to correct the values inside the components data ndarray according to whether indices are covariant or contravariant. It uses the metric matrix to lower values of covariant indices. )rarcrrGrTrrL)rrKrHrIinverserVrs r4rz8_TensorDataLazyEvaluator._correct_signature_from_indicess!) GAt::g/EEdDLbLbLgLgijkZZG/EE,;;D.sorted_compos+;;KU Ur3)rr)r[r\r]s`` r4add_rearrange_tensmul_partsz4_TensorDataLazyEvaluator.add_rearrange_tensmul_partss  VEQN 44[Ar3cddlm}t||s6t|dk(r t |ddr||d|d}|S||}|S)a Transform ``data`` to array. The parameter ``data`` may contain data in various formats, e.g. nested lists, SymPy ``Matrix``, and so on. Examples ======== >>> from sympy.tensor.tensor import _TensorDataLazyEvaluator >>> _TensorDataLazyEvaluator.parse_data([1, 3, -6, 12]) [1, 3, -6, 12] >>> _TensorDataLazyEvaluator.parse_data([[1, 2], [4, 7]]) [[1, 2], [4, 7]] r`rr@r__call__)rrrrLhasattr)rrs r4r/z#_TensorDataLazyEvaluator.parse_datasV" 1$ 564yA~'$q':">,T!Wd1g> -T2 r3Nr)rrrrr__annotations__rrrrrrrr!r-r5r9r;rBrGrLr=rrXr^r/r2r3r4rrs"+-,244 Qf00  "#)>->*/I<06666( SS r3rcPeZdZdZdZdZedZdZdZ dZ dZ d Z d Z y ) _TensorManagera Class to manage tensor properties. Notes ===== Tensors belong to tensor commutation groups; each group has a label ``comm``; there are predefined labels: ``0`` tensors commuting with any other tensor ``1`` tensors anticommuting among themselves ``2`` tensors not commuting, apart with those with ``comm=0`` Other groups can be defined using ``set_comm``; tensors in those groups commute with those with ``comm=0``; by default they do not commute with any other group. c$|jyrY _comm_initrns r4rQz_TensorManager.__init__,s  r3cJtdDcgc]}ic}|_tdD]&}d|jd|<d|j|d<(d|jdd<d|jdd<d|jdd<dddd|_dddd|_ycc}w)Nrr`r@)rr`r@)r_comm_comm_symbols2i_comm_i2symbolrOrVs r4rhz_TensorManager._comm_init/s"'(+Qb+ q !A DJJqM!  DJJqM!  ! 1 a 1 a 1 a"#qA!"a1o,s B c|jSrY)rkrns r4rz_TensorManager.comm: zzr3c$||jvrtt|j}|jjid|j|d<d|jd|<||j|<||j|<|S|j|S)z Get the commutation group number corresponding to ``i``. ``i`` can be a symbol or a number or a string. If ``i`` is not already defined its commutation group number is set. r)rlrLrkrerm)rOrVrfs r4comm_symbols2iz_TensorManager.comm_symbols2i>s D(( (DJJA JJ  b ! DJJqM!  DJJqM! &'D  #%&D   "H##A&&r3c |j|S)zS Returns the symbol corresponding to the commutation group number. )rmrns r4 comm_i2symbolz_TensorManager.comm_i2symbolQs""1%%r3c|dvr tdt|}t|}||jvrrt|j}|jj id|j|d<d|jd|<||j|<||j |<||jvrrt|j}|jj id|jd|<d|j|d<||j|<||j |<|j|}|j|}||j||<||j||< ty)a Set the commutation parameter ``c`` for commutation groups ``i, j``. Parameters ========== i, j : symbols representing commutation groups c : group commutation number Notes ===== ``i, j`` can be symbols, strings or numbers, apart from ``0, 1`` and ``2`` which are reserved respectively for commuting, anticommuting tensors and tensors not commuting with any other group apart with the commuting tensors. For the remaining cases, use this method to set the commutation rules; by default ``c=None``. The group commutation number ``c`` is assigned in correspondence to the group commutation symbols; it can be 0 commuting 1 anticommuting None no commutation property Examples ======== ``G`` and ``GH`` do not commute with themselves and commute with each other; A is commuting. >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, TensorManager, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz') >>> i0,i1,i2,i3,i4 = tensor_indices('i0:5', Lorentz) >>> A = TensorHead('A', [Lorentz]) >>> G = TensorHead('G', [Lorentz], TensorSymmetry.no_symmetry(1), 'Gcomm') >>> GH = TensorHead('GH', [Lorentz], TensorSymmetry.no_symmetry(1), 'GHcomm') >>> TensorManager.set_comm('Gcomm', 'GHcomm', 0) >>> (GH(i1)*G(i0)).canon_bp() G(i0)*GH(i1) >>> (G(i1)*G(i0)).canon_bp() G(i1)*G(i0) >>> (G(i1)*A(i0)).canon_bp() A(i0)*G(i1) )rr`Nz+`c` can assume only the values 0, 1 or NonerN)rdrrlrLrkrermr)rOrVrcrfninjs r4set_commz_TensorManager.set_commWs]d L JK K AJ AJ D(( (DJJA JJ  b ! DJJqM!  DJJqM! &'D  #%&D   " D(( (DJJA JJ  b ! DJJqM!  DJJqM! &'D  #%&D   "  ! !! $  ! !! $ 2r 2r  r3c@|D]\}}}|j|||y)z Set the commutation group numbers ``c`` for symbols ``i, j``. Parameters ========== args : sequence of ``(i, j, c)`` N)ry)rOrrVrrvs r4 set_commsz_TensorManager.set_commss) #GAq! MM!Q " #r3c`|j|j||dk(s|dk(rdSdS)z Return the commutation parameter for commutation group numbers ``i, j`` see ``_TensorManager.set_comm`` rN)rkget)rOrVrs r4rz_TensorManager.get_comms3 zz!}  a16ADDtDDr3c$|jy)z* Clear the TensorManager. Nrgrns r4clearz_TensorManager.clears r3N)rrrrrQrhpropertyrrrrtryr{rrr2r3r4reresF& .'&& Qf #Er3recDeZdZdZ ddZedZedZedZedZ e dZ e d Z e d Z d Zd Zd ZeZedZej&dZej(dZeddddZeddddZdZy)r%aH A TensorIndexType is characterized by its name and its metric. Parameters ========== name : name of the tensor type dummy_name : name of the head of dummy indices dim : dimension, it can be a symbol or an integer or ``None`` eps_dim : dimension of the epsilon tensor metric_symmetry : integer that denotes metric symmetry or ``None`` for no metric metric_name : string with the name of the metric tensor Attributes ========== ``metric`` : the metric tensor ``delta`` : ``Kronecker delta`` ``epsilon`` : the ``Levi-Civita epsilon`` tensor ``data`` : (deprecated) a property to add ``ndarray`` values, to work in a specified basis. Notes ===== The possible values of the ``metric_symmetry`` parameter are: ``1`` : metric tensor is fully symmetric ``0`` : metric tensor possesses no index symmetry ``-1`` : metric tensor is fully antisymmetric ``None``: there is no metric tensor (metric equals to ``None``) The metric is assumed to be symmetric by default. It can also be set to a custom tensor by the ``.set_metric()`` method. If there is a metric the metric is used to raise and lower indices. In the case of non-symmetric metric, the following raising and lowering conventions will be adopted: ``psi(a) = g(a, b)*psi(-b); chi(-a) = chi(b)*g(-b, -a)`` From these it is easy to find: ``g(-a, b) = delta(-a, b)`` where ``delta(-a, b) = delta(b, -a)`` is the ``Kronecker delta`` (see ``TensorIndex`` for the conventions on indices). For antisymmetric metrics there is also the following equality: ``g(a, -b) = -delta(a, -b)`` If there is no metric it is not possible to raise or lower indices; e.g. the index of the defining representation of ``SU(N)`` is 'covariant' and the conjugate representation is 'contravariant'; for ``N > 2`` they are linearly independent. ``eps_dim`` is by default equal to ``dim``, if the latter is an integer; else it can be assigned (for use in naive dimensional regularization); if ``eps_dim`` is not an integer ``epsilon`` is ``None``. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> Lorentz.metric metric(Lorentz,Lorentz) Nc Fd|vr|d}td|ddd|}t|tr t|}|t|d}t|tr t|}|td|jz}n t |}||}n t |}t |}t|tr t|}d |vr;t d dd |jd } | | d vrd }n | j}| rd }nd}tj|||||||} g| _ | S)N dummy_fmtzh The dummy_fmt keyword to TensorIndexType is deprecated. Use dummy_name=z instead. r7z$deprecated-tensorindextype-dummy-fmtr.r/rdim_rz The 'metric' keyword argument to TensorIndexType is deprecated. Use the 'metric_symmetry' keyword argument or the TensorIndexType.set_metric() method instead. z!deprecated-tensorindextype-metric)TFrr`rr`) r%rr~rrbrr&r}r__new___autogenerated) clsrbrr1eps_dimmetric_symmetry metric_namekwargsrrobjs r4rzTensorIndexType.__new__sS & {+I %%;'*/+Q  #J dC $!114<<-@%$ !4h??r3c||_yrY)_metric)rOrs r4 set_metriczTensorIndexType.set_metric|s  r3c4|j|jkSrYrbrOothers r4__lt__zTensorIndexType.__lt__syy5::%%r3c|jSrYrrns r4rzTensorIndexType.__str__s yyr3crttt5t|cdddS#1swYyxYwrYr5r'r&_tensor_data_substitution_dictrns r4rzTensorIndexType.data. 4 5 81$7 8 8 8 -6ctddlm}tj |}|j dkDr t d|j dk(r||jjr)|jd}||jk7r t d|jd}|j||}t|D] \}}||||f<|}|j\}} || k7r t d|jjr|j|k7r t d|t|<tj|j|tt 5|j#} dddt%d|} t%d |} tt 5tj t'| | | _dddy#1swYexYw#1swYyxYw) Nr`r`r@z1data have to be of rank 1 (diagonal metric) or 2.rzDimension mismatchzNon-square matrix tensor.i1i2)r5rrrr/rrdr1r2r0zerosrarrBrr'r&get_kronecker_deltarrr$r) rOrrnda_dimr1 newndarrayrVvaldim1dim2rrrs r4rzTensorIndexType.datas 1'2248 99;?PQ Q 99;! xx!!**Q-dhh&$%9::**Q-C.44S#>J#D/ '3#& 1a4  'DZZ d 4<89 9 88  xx4 !566/3&t,&66t{{DI 4 5 /,,.E / t $ t $ 4 5 Q":"E"Ec$i"PE"rcN  Q Q  / / Q QsG,GGG$cttt5|tvrt|=|jtvrt|j=dddy#1swYyxYwrYr5r'r&rrrns r4rzTensorIndexType.dataT 4 5 @55248{{<<24;;?  @ @ @ 3AAz The TensorIndexType.get_kronecker_delta() method is deprecated. Use the TensorIndexType.delta attribute instead. r7z"deprecated-tensorindextype-methodsrcPttd}td|gdz|}|S)Nr@r)rrr)rOsym2rs r4rz#TensorIndexType.get_kronecker_deltas-5a894$40 r3z The TensorIndexType.get_epsilon() method is deprecated. Use the TensorIndexType.epsilon attribute instead. ct|jttfsyt t |jd}t d|g|jz|}|S)Nr`r)r_eps_dimr#r rrr)rOrrs r4 get_epsilonzTensorIndexType.get_epsilonsN$--*g)>?4T]]AFGUTF4==$8#>r3c|tvrt|=d}||jddf||jddf||jddf||jddf|j}|tvrt|=yy)z EXPERIMENTAL: do not rely on this API method. This destroys components data associated to the ``TensorIndexType``, if any, specifically: * metric tensor data * Kronecker tensor data cL|tjvrtj|=yyrY)rrrFs r4delete_tensmul_datazJTensorIndexType._components_data_full_destroy..delete_tensmul_datas%4PPP2NNsSQr3TFN)rrr)rOrrs r4_components_data_full_destroyz-TensorIndexType._components_data_full_destroys 1 1.t4 T T[[$56T[[$67T[[%67T[[%78((* 2 2.u5 3r3)NNNr`r)rrrrrrrbrr1rr(rrrrrrrrsetterdeleterr)rrrr2r3r4r%r%sYCJ?C/7AF!!!! ; ;MM@@ &H88  [["Q"QH \\@@ "'#G   "'#G 6r3r%cZeZdZdZd dZedZedZedZdZ dZ dZ y ) rraq Represents a tensor index Parameters ========== name : name of the index, or ``True`` if you want it to be automatically assigned tensor_index_type : ``TensorIndexType`` of the index is_up : flag for contravariant index (is_up=True by default) Attributes ========== ``name`` ``tensor_index_type`` ``is_up`` Notes ===== Tensor indices are contracted with the Einstein summation convention. An index can be in contravariant or in covariant form; in the latter case it is represented prepending a ``-`` to the index name. Adding ``-`` to a covariant (is_up=False) index makes it contravariant. Dummy indices have a name with head given by ``tensor_inde_type.dummy_name`` with underscore and a number. Similar to ``symbols`` multiple contravariant indices can be created at once using ``tensor_indices(s, typ)``, where ``s`` is a string of names. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, TensorIndex, TensorHead, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> mu = TensorIndex('mu', Lorentz, is_up=False) >>> nu, rho = tensor_indices('nu, rho', Lorentz) >>> A = TensorHead('A', [Lorentz, Lorentz]) >>> A(mu, nu) A(-mu, nu) >>> A(-mu, -rho) A(mu, -rho) >>> A(mu, -mu) A(-L_0, L_0) cZt|tr t|}nmt|tr|}nZ|durKdjt |j }t|}|j j |n tdt|}tj||||SNTz_i{} invalid name rr~rrrLrrerdrrr)rrbrTrc name_symbols r4rzTensorIndex.__new__(s dC  ,K f %K T\==%6%E%E!FGD ,K  , , 3 3K @^, ,}}S+/@%HHr3c4|jdjSrBrrns r4rbzTensorIndex.name7rr3c |jdSrrrns r4rTzTensorIndex.tensor_index_type;rr3c |jdSrrrns r4rczTensorIndex.is_up?rr3c@|j}|jsd|z}|S)Nz-%s)rbrc)rOss r4_printzTensorIndex._printCs IIzz Ar3cd|j|jf|j|jfkSrY)rTrbrs r4rzTensorIndex.__lt__Is0''3((%**56 7r3c^t|j|j|j }|SrY)rrrbrTrcrOt1s r4__neg__zTensorIndex.__neg__Ms( D$:$:ZZ" r3NT) rrrrrrrbrTrcrrrr2r3r4rrrrsY0b I!! 7r3rrct|tr%t|dDcgc]}|j}}n t d|Dcgc]}t ||}}t |dk(r|dS|Scc}wcc}w)a Returns list of tensor indices given their names and their types. Parameters ========== s : string of comma separated names of indices typ : ``TensorIndexType`` of the indices Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz) Tseqexpecting a stringr`r)rr~rrbrdrrrL)rrirDrrVtilists r4tensor_indicesrSsw$!S$QD1 2QVV 2 2-..+, -ak!S! -F - 6{aay M 3.s A/A4ceZdZdZdZedZedZedZe dZ e dZ e dZ e d Z y ) ra Monoterm symmetry of a tensor (i.e. any symmetric or anti-symmetric index permutation). For the relevant terminology see ``tensor_can.py`` section of the combinatorics module. Parameters ========== bsgs : tuple ``(base, sgs)`` BSGS of the symmetry of the tensor Attributes ========== ``base`` : base of the BSGS ``generators`` : generators of the BSGS ``rank`` : rank of the tensor Notes ===== A tensor can have an arbitrary monoterm symmetry provided by its BSGS. Multiterm symmetries, like the cyclic symmetry of the Riemann tensor (i.e., Bianchi identity), are not covered. See combinatorics module for information on how to generate BSGS for a general index permutation group. Simple symmetries can be generated using built-in methods. See Also ======== sympy.combinatorics.tensor_can.get_symmetric_group_sgs Examples ======== Define a symmetric tensor of rank 2 >>> from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, TensorHead >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> sym = TensorSymmetry(get_symmetric_group_sgs(2)) >>> T = TensorHead('T', [Lorentz]*2, sym) Note, that the same can also be done using built-in TensorSymmetry methods >>> sym2 = TensorSymmetry.fully_symmetric(2) >>> sym == sym2 True ct|dk(r |d\}}nt|dk(r|\}}n tdt|tst|}t|tst|}t j |||fi|S)Nr`rr@z:bsgs required, either two separate parameters or one tuple)rL TypeErrorrrrr)rrkw_argsrrs r4rzTensorSymmetry.__new__s{ t9>#Aw D* Y!^# D*XY Y$&$g>>r3c |jdSrBrrns r4rzTensorSymmetry.baserr3c |jdSrrrns r4rzTensorSymmetry.generatorsrr3c:|jdjdz S)Nrr@)rsizerns r4rzTensorSymmetry.ranksq!&&**r3c|dkDrt|d}t|S|dkrt| d}t|S|dk(rgtdgf}tS)z Returns a fully symmetric (antisymmetric if ``rank``<0) TensorSymmetry object for ``abs(rank)`` indices. rFTr`)rr r)rrbsgss r4rzTensorSymmetry.fully_symmetricsm !8*47D d## AX*D5$7Dd##QYQ()Dd##r3cgtdg}}|D]8}|dkDr t|d}n|dkrt| d}n)t||g|\}}:t||S)a Returns a TensorSymmetry object that is being a direct product of fully (anti-)symmetric index permutation groups. Notes ===== Some examples for different values of ``(*args)``: ``(1)`` vector, equivalent to ``TensorSymmetry.fully_symmetric(1)`` ``(2)`` tensor with 2 symmetric indices, equivalent to ``.fully_symmetric(2)`` ``(-2)`` tensor with 2 antisymmetric indices, equivalent to ``.fully_symmetric(-2)`` ``(2, -2)`` tensor with the first 2 indices commuting and the last 2 anticommuting ``(1, 1, 1)`` tensor with 3 indices without any symmetry r`rFT)r rrr)rrrsgsr bsgs2s r4direct_productzTensorSymmetry.direct_productsr Q(c ?CQw/U;q/d;+D#>>ID# ?dC((r3c ttS)zD Returns a monotorem symmetry of the Riemann tensor )rr)rs r4riemannzTensorSymmetry.riemanns l++r3c4tgt|dzgS)zM TensorSymmetry object for ``rank`` indices with no symmetry r`)rr )rrs r4rzTensorSymmetry.no_symmetrys b;tAv#6"788r3N)rrrrrrrrr classmethodrrrrr2r3r4rrps.^ ?++ $ $))6,, 99r3rzf The tensorsymmetry() function is deprecated. Use the TensorSymmetry constructor instead. r7zdeprecated-tensorsymmetryrcFddlm}d}|s#ttt|dSt |dk(rt |dd|r t|S||d\}}|ddD]}||\}}t ||||\}} tt||S)ay Returns a ``TensorSymmetry`` object. This method is deprecated, use ``TensorSymmetry.direct_product()`` or ``.riemann()`` instead. Explanation =========== One can represent a tensor with any monoterm slot symmetry group using a BSGS. ``args`` can be a BSGS ``args[0]`` base ``args[1]`` sgs Usually tensors are in (direct products of) representations of the symmetric group; ``args`` can be a list of lists representing the shapes of Young tableaux Notes ===== For instance: ``[[1]]`` vector ``[[1]*n]`` symmetric tensor of rank ``n`` ``[[n]]`` antisymmetric tensor of rank ``n`` ``[[2, 2]]`` monoterm slot symmetry of the Riemann tensor ``[[1],[1]]`` vector*vector ``[[2],[1],[1]`` (antisymmetric tensor)*vector*vector Notice that with the shape ``[2, 2]`` we associate only the monoterm symmetries of the Riemann tensor; this is an abuse of notation, since the shape ``[2, 2]`` corresponds usually to the irreducible representation characterized by the monoterm symmetries and by the cyclic symmetry. rr ct|dk(r|d}t|d}|Std|Drt|}t|}|S|ddgk(rt}|St)Nr`rc3&K|] }|dk( yw)r`Nr2rrDs r4rz7tensorsymmetry..tableau2bsgs..(s%a16%sr@)rLrrrNotImplementedError)rrfrs r4 tableau2bsgsz$tensorsymmetry..tableau2bsgs"st q6Q;!A*1a0D %1%%F.q1  q!f# *)r3r`r@N)sympy.combinatoricsr rrrLrr)rr rrrrbasexsgsxs r4tensorsymmetryrsX0 egu[^'<== 4yA~*T!WQZ=d##T!W%ID# !"X@"1o t'c5$? c@ %c* ++r3z5TensorType is deprecated. Use tensor_heads() instead.zdeprecated-tensortypecXeZdZdZdZdZedZedZedZ dZ d dZ y ) TensorTypeaa Class of tensor types. Deprecated, use tensor_heads() instead. Parameters ========== index_types : list of ``TensorIndexType`` of the tensor indices symmetry : ``TensorSymmetry`` of the tensor Attributes ========== ``index_types`` ``symmetry`` ``types`` : list of ``TensorIndexType`` without repetitions Fc v|jt|k(sJtj|t ||fi|}|SrY)rrLrrr)rrJrrrs r4rzTensorType.__new__Us:}}K 0000mmC !4hJ'J r3c |jdSrBrrns r4rJzTensorType.index_typesZrr3c |jdSrrrns r4rzTensorType.symmetry^rr3cDtt|jdS)Nc|jSrYrrCs r4rEz"TensorType.types..ds 166r3rF)rsetrJrns r4typeszTensorType.typesbsc$**+1ABBr3cXd|jDcgc] }t|c}zScc}w)NzTensorType(%s))rJr~rOrDs r4rzTensorType.__str__fs%D4D4D#EqCF#EFF#Es'c \t|tr%t|dDcgc]}|j}}n t dt |dk(r%t |d|j|j|S|Dcgc]$}t ||j|j|&c}Scc}wcc}w)z Return a TensorHead object or a list of TensorHead objects. Parameters ========== s : name or string of names. comm : Commutation group. see ``_TensorManager.set_comm`` Trrr`r) rr~rrbrdrLrrJr)rOrrrDnamesrbs r4razTensorType.__call__is a %,QD%9:QVV:E:12 2 u:?eAh(8(8$--N NX]^PTJtT%5%5t}}dK^ ^ ; _s B$8)B)N)r) rrrris_commutativerrrJrrrrar2r3r4rr=s]  N CCG_r3rzN The tensorhead() function is deprecated. Use tensor_heads() instead. zdeprecated-tensorheadc|"tt|Dcgc]}dg}}tt5t |}dddt ||||Scc}w#1swYxYw)ao Function generating tensorhead(s). This method is deprecated, use TensorHead constructor or tensor_heads() instead. Parameters ========== name : name or sequence of names (as in ``symbols``) typ : index types sym : same as ``*args`` in ``tensorsymmetry`` comm : commutation group number see ``_TensorManager.set_comm`` Nr`)rrLr'r&rr)rbrirrrVs r4rrsd0 {!#c(O,qs,, 0 1#c"# dCd ++-##s A AA!ceZdZdZdZddZedZedZedZ edZ ed Z d Z d Z d Zd ZdZedZej$dZej&dZdZdZy)rat Tensor head of the tensor. Parameters ========== name : name of the tensor index_types : list of TensorIndexType symmetry : TensorSymmetry of the tensor comm : commutation group number Attributes ========== ``name`` ``index_types`` ``rank`` : total number of indices ``symmetry`` ``comm`` : commutation group Notes ===== Similar to ``symbols`` multiple TensorHeads can be created using ``tensorhead(s, typ, sym=None, comm=0)`` function, where ``s`` is the string of names and ``sym`` is the monoterm tensor symmetry (see ``tensorsymmetry``). A ``TensorHead`` belongs to a commutation group, defined by a symbol on number ``comm`` (see ``_TensorManager.set_comm``); tensors in a commutation group have the same commutation properties; by default ``comm`` is ``0``, the group of the commuting tensors. Examples ======== Define a fully antisymmetric tensor of rank 2: >>> from sympy.tensor.tensor import TensorIndexType, TensorHead, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> asym2 = TensorSymmetry.fully_symmetric(-2) >>> A = TensorHead('A', [Lorentz, Lorentz], asym2) Examples with ndarray values, the components data assigned to the ``TensorHead`` object are assumed to be in a fully-contravariant representation. In case it is necessary to assign components data which represents the values of a non-fully covariant tensor, see the other examples. >>> from sympy.tensor.tensor import tensor_indices >>> from sympy import diag >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> i0, i1 = tensor_indices('i0:2', Lorentz) Specify a replacement dictionary to keep track of the arrays to use for replacements in the tensorial expression. The ``TensorIndexType`` is associated to the metric used for contractions (in fully covariant form): >>> repl = {Lorentz: diag(1, -1, -1, -1)} Let's see some examples of working with components with the electromagnetic tensor: >>> from sympy import symbols >>> Ex, Ey, Ez, Bx, By, Bz = symbols('E_x E_y E_z B_x B_y B_z') >>> c = symbols('c', positive=True) Let's define `F`, an antisymmetric tensor: >>> F = TensorHead('F', [Lorentz, Lorentz], asym2) Let's update the dictionary to contain the matrix to use in the replacements: >>> repl.update({F(-i0, -i1): [ ... [0, Ex/c, Ey/c, Ez/c], ... [-Ex/c, 0, -Bz, By], ... [-Ey/c, Bz, 0, -Bx], ... [-Ez/c, -By, Bx, 0]]}) Now it is possible to retrieve the contravariant form of the Electromagnetic tensor: >>> F(i0, i1).replace_with_arrays(repl, [i0, i1]) [[0, -E_x/c, -E_y/c, -E_z/c], [E_x/c, 0, -B_z, B_y], [E_y/c, B_z, 0, -B_x], [E_z/c, -B_y, B_x, 0]] and the mixed contravariant-covariant form: >>> F(i0, -i1).replace_with_arrays(repl, [i0, -i1]) [[0, E_x/c, E_y/c, E_z/c], [E_x/c, 0, B_z, -B_y], [E_y/c, -B_z, 0, B_x], [E_z/c, B_y, -B_x, 0]] Energy-momentum of a particle may be represented as: >>> from sympy import symbols >>> P = TensorHead('P', [Lorentz], TensorSymmetry.no_symmetry(1)) >>> E, px, py, pz = symbols('E p_x p_y p_z', positive=True) >>> repl.update({P(i0): [E, px, py, pz]}) The contravariant and covariant components are, respectively: >>> P(i0).replace_with_arrays(repl, [i0]) [E, p_x, p_y, p_z] >>> P(-i0).replace_with_arrays(repl, [-i0]) [E, -p_x, -p_y, -p_z] The contraction of a 1-index tensor by itself: >>> expr = P(i0)*P(-i0) >>> expr.replace_with_arrays(repl, []) E**2 - p_x**2 - p_y**2 - p_z**2 FNc @t|tr t|}nt|tr|}n td|tj t |}n|jt |k(sJtj||t||t|}|S)Nr) rr~rrdrrrLrrrrr)rrbrJrrrrs r4rzTensorHead.__new__s dC  ,K f %K^, ,  %11#k2BCH==C $44 44mmCe[.A8WUY][ r3c4|jdjSrBrrns r4rbzTensorHead.name!rr3c2t|jdSrrrrns r4rJzTensorHead.index_types%sDIIaL!!r3c |jdSrrrns r4rzTensorHead.symmetry)rr3cFtj|jdSr)rrrrrns r4rzTensorHead.comm-s++DIIaL99r3c,t|jSrY)rLrJrns r4rzTensorHead.rank1s4##$$r3cd|j|jf|j|jfkSrY)rbrJrs r4rzTensorHead.__lt__5s+ 4++, E>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorSymmetry, TensorHead >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> A = TensorHead('A', [Lorentz]*2, TensorSymmetry.no_symmetry(2)) >>> t = A(a, -b) >>> t A(a, -b)  -r`NFrc) rrJrr~stripreplace startswithrerrrLrdoit)rOrKrupdated_indicesrsrirs r4razTensorHead.__call__Ds>GT%5%56 ,HC#s#iik))#r2>>#&#**;s12w16,89$**;sC+@A&&s+ , 73#7#899o99{{}r3c ttt5|j t dddlm}m}|jDcgc]}|j}}|j}|j}|D] }||||}||d|f|dz|dzf}"||tjzzcdddScc}w#1swYyxYw)NzNo power on abstract tensors.r`rDrr@) r5r'r&rrdrrrrJrrHalf) rOrrrrzmetricsmarray marraydimrs r4__pow__zTensorHead.__pow__vs 4 5 .yy  !@AA ?'+'7'78!qvv8G8YYF I! _&vvv>*6Ay>IaKQZ[\Q\C]^ _eaffn- . .9 . .s.CB=AC=CC crttt5t|cdddS#1swYyxYwrYrrns r4rzTensorHead.datarrcrttt5|t|<dddy#1swYyxYwrYrrOrs r4rzTensorHead.data1 4 5 837 *4 0 8 8 8 -6c8t|tvrt|=yyrY)r5rrns r4rzTensorHead.datas 1 1.t4 2r3cttt5|jj cdddS#1swYyxYwrYr5r'r&r__iter__rns r4r4zTensorHead.__iter__6 4 5 (99%%' ( ( ( >AcXt|jdk(ry|tvrt|=yy)z EXPERIMENTAL: do not rely on this API method. Destroy components data associated to the ``TensorHead`` object, this checks for attached components data, and destroys components data too. rN)r5rbrrns r4rz(TensorHead._components_data_full_destroys1  99   1 1.t4 2r3rB)rrrrr rrrbrJrrrrrrrar+rrrr4rr2r3r4rrsn^N !!""::%%OS.d. 88  [[88  \\55 ( 5r3rc t|tr%t|dDcgc]}|j}}n t d|Dcgc]}t ||||}}t |dk(r|dS|Scc}wcc}w)z= Returns a sequence of TensorHeads from a string `s` Trrr`r)rr~rrbrdrrL)rrJrrrDrrbthlists r4 tensor_headsr:s|!S!(!56A66-..HM Nj{Hd; NF N 6{aay M7Os A1A6cbeZdZdZdZdZdZdZdZdZ dZ d Z d Z d Z d Zd ZdZdZeedZeedZedZed%dZed&dZdZdZedZdZdZdZedZ edZ!edZ"edZ#edZ$d'd!Z%d"Z&d#Z'd(d$Z(y ))ra Abstract base class for tensor expressions Notes ===== A tensor expression is an expression formed by tensors; currently the sums of tensors are distributed. A ``TensExpr`` can be a ``TensAdd`` or a ``TensMul``. ``TensMul`` objects are formed by products of component tensors, and include a coefficient, which is a SymPy expression. In the internal representation contracted indices are represented by ``(ipos1, ipos2, icomp1, icomp2)``, where ``icomp1`` is the position of the component tensor with contravariant index, ``ipos1`` is the slot which the index occupies in that component tensor. Contracted indices are therefore nameless in the internal representation. g(@Fc(|tjzSrY)r NegativeOnerns r4rzTensExpr.__neg__sAMM!!r3ctrYrrns r4__abs__zTensExpr.__abs__!!r3c:t||jdSNFdeeprr$rs r4__add__zTensExpr.__add__stU#((e(44r3c:t||jdSrCrFrs r4__radd__zTensExpr.__radd__ud#((e(44r3c<t|| jdSrCrFrs r4__sub__zTensExpr.__sub__steV$))u)55r3c<t|| jdSrCrFrs r4__rsub__zTensExpr.__rsub__sute$))u)55r3c:t||jdS)a Multiply two tensors using Einstein summation convention. Explanation =========== If the two tensors have an index in common, one contravariant and the other covariant, in their product the indices are summed Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensor_heads('p,q', [Lorentz]) >>> t1 = p(m0) >>> t2 = q(-m0) >>> t1*t2 p(L_0)*q(-L_0) FrDrr$rs r4__mul__zTensExpr.__mul__s.tU#((e(44r3c:t||jdSrCrPrs r4__rmul__zTensExpr.__rmul__rJr3ct|}t|tr tdt |t j |z jdS)Ncannot divide by a tensorFrD)r!rrrdrrOner$rs r4 __truediv__zTensExpr.__truediv__ sD eX &89 9tQUU5[)..E.::r3ctd)NrU)rdrs r4 __rtruediv__zTensExpr.__rtruediv__s455r3c ttt5|j t dddlm}m}|j}|j}|j}|D]5}||||djj|d|f|dz|dzf}7||tjzzcdddS#1swYyxYw)NzNo power without ndarray data.r`rDrr@) r5r'r&rrdrrrrHrrTrr')rOrrrrHr)rErs r4r+zTensExpr.__pow__s 4 5 .yy  !ABB ?99DYYF;;=D *!1I//44IQQ/  eaffn- . . .s BB;;CctrYr?rs r4__rpow__zTensExpr.__rpow__%rAr3ctdNzabstract methodr?rns r4nocoeffzTensExpr.nocoeff(""344r3ctdr^r?rns r4r zTensExpr.coeff-r`r3ctdr^r?rns r4rozTensExpr.get_indices2!"344r3ctdr^r?rns r4rzTensExpr.get_free_indices6rcr3ctdr^r?rOrepls r4_replace_indiceszTensExpr._replace_indices:rcr3c4t|j|SrY)r9substitute_indices)rO index_tupless r4fun_evalzTensExpr.fun_eval>s&t&& 55r3cVddlm}ttt5d|j cxkrdkrn td|j jd}|j dk(r|j jdnd}|j dk(rPg|z}t|D]<}|jgt|D]}||j|||f>ndg|z}t|D] }||||< ||cdddStd#1swYyxYw)z DEPRECATED: do not use. Returns ndarray components data as a matrix, if components data are available and ndarray dimension does not exceed 2. r)Matrixr@r`Nz-missing multidimensional reduction to matrix.) sympy.matrices.densernr5r'r&rrr0rrer)rOrnrowscolumnsmat_listrVrs r4 get_matrixzTensExpr.get_matrixBs5 0 4 5 E499!!*CEEyyq)04 Q$))//!,A99>!DyH"4[; +!&w;A$QK..tAqDz:;; !%v}H"4[.&*1g .h' E E *CEE! E EsC*D DD(cJ|Dcgc]}|j|c}Scc}wrYrh)indices1indices2rVs r4_get_indices_permutationz!TensExpr._get_indices_permutation^s+34aq!444s ct}|jD]2}t|ts|j |j 4|SrY)rrrrupdate_get_free_indices_setrOindsetr s r4r{zTensExpr._get_free_indices_setbsB99 ;C#x( c779: ; r3ct}|jD]2}t|ts|j |j 4|SrY)rrrrrz_get_dummy_indices_setr|s r4rzTensExpr._get_dummy_indices_setisB99 .recursor{sq$ ,9$+%%D5("34' 27FAs'S!X666756AA,!A*" A,r2)r)rOrrs @@r4_iterate_dummy_indiceszTensExpr._iterate_dummy_indicesws%//1  7b!!r3cD|jfd|dS)Nc3Kt|tr |vr||fyyt|ttfr3t |j D]\}}|||fzEd{yy7wrYr)rrlrr free_setrs r4rz0TensExpr._iterate_free_indices..recursorsq$ ,8#+%$D5("34' 27FAs'S!X666756rr2)r{)rOrrs @@r4_iterate_free_indiceszTensExpr._iterate_free_indicess%--/ 7b!!r3c fd|dS)Nc3Kt|tr||fyt|ttfr3t |j D]\}}|||fzEd{yy7wrYr)rrlrr rs r4rz+TensExpr._iterate_indices..recursorsd$ ,Sk!D5("34' 27FAs'S!X666756sAA'A% A'r2r2)rOrs @r4_iterate_indiceszTensExpr._iterate_indicess 7b!!r3cddlm}m}m}||||dd|zf}t t |}|||dc|d<||<|||S)Nr`)rrrr@r)rrrrrr)rrrlr1rrrpermus r4!_contract_and_permute_with_metricz*TensExpr._contract_and_permute_with_metrics] IH!-">AcE KU3Z $Sz58a%*5%((r3c jddlm}|Dcgc]}|j}}g}g}|dd} t|D]\} } | | vr| j | } d| | <!| | vrL| j | } d| | <| || <| j r|j | `|j | r| | } | td|d|d| | <| || <| j | j z s| j r|j | |j | tt|t|zt|krtd|d||D]Q}||}||vr td||}tj|}tj|||t|}S|D]<}||}||vr td||}tj|||t|}>|rtj||}|||}t|dr|j!dk(r|d}||fScc}w) Nr`rzincompatible indices: z and z!No metric provided to lower indexrrr2)rrrTrarhrcrerdrLrrrLrrrxrbr)r free_ind1 free_ind2replacement_dictrrV index_types1pos2uppos2downfree2remainingruindex1rvindex2rlindex_type_posrmetric_inversers r4 _match_indices_with_other_tensorz)TensExpr._match_indices_with_other_tensorsh&5>?++? ?"1%i0 .LD&'%++F3'+t$w.(%++VG4'+t$"( $<<MM$'OOD)'->$)U^%_``'+t$"( $<<&,,.|| d+ -/ .2 s9~I. /#i. @)YWX X kC)#.N%55 !DEE%n5F5DDVLN>>~uVY[^_h[ijE  k cC)#.N%55 !DEE%n5F>>vucSVW`SabE  c ";;IyQK{3E 5& !ejjla&7"IE%s@sH0Nc ddlm}|xsg}|jDcic].}|jr|jdn|jd |0}}t |D]3\}}t |ttfs||vr ||||<*||  ||<5|jD cic]\}} ||| }}} |jD]\}} t |tr#tdDcgc]}|j} }n#|jD cgc]} | j} } t| | jk7s't!dt#| | j$Drt'd|d| d| j$|j)|\} } |j+| || |\} } | Scc}wcc} }wcc}wcc} w) aA Replace the tensorial expressions with arrays. The final array will correspond to the N-dimensional array with indices arranged according to ``indices``. Parameters ========== replacement_dict dictionary containing the replacement rules for tensors. indices the index order with respect to which the array is read. The original index order will be used if no value is passed. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices >>> from sympy.tensor.tensor import TensorHead >>> from sympy import symbols, diag >>> L = TensorIndexType("L") >>> i, j = tensor_indices("i j", L) >>> A = TensorHead("A", [L]) >>> A(i).replace_with_arrays({A(i): [1, 2]}, [i]) [1, 2] Since 'indices' is optional, we can also call replace_with_arrays by this way if no specific index order is needed: >>> A(i).replace_with_arrays({A(i): [1, 2]}) [1, 2] >>> expr = A(i)*A(j) >>> expr.replace_with_arrays({A(i): [1, 2]}) [[1, 2], [2, 4]] For contractions, specify the metric of the ``TensorIndexType``, which in this case is ``L``, in its covariant form: >>> expr = A(i)*A(-i) >>> expr.replace_with_arrays({A(i): [1, 2], L: diag(1, -1)}) -3 Symmetrization of an array: >>> H = TensorHead("H", [L, L]) >>> a, b, c, d = symbols("a b c d") >>> expr = H(i, j)/2 + H(j, i)/2 >>> expr.replace_with_arrays({H(i, j): [[a, b], [c, d]]}) [[a, b/2 + c/2], [b/2 + c/2, d]] Anti-symmetrization of an array: >>> expr = H(i, j)/2 - H(j, i)/2 >>> repl = {H(i, j): [[a, b], [c, d]]} >>> expr.replace_with_arrays(repl) [[0, b/2 - c/2], [-b/2 + c/2, 0]] The same expression can be read as the transpose by inverting ``i`` and ``j``: >>> expr.replace_with_arrays(repl, [j, i]) [[0, -b/2 + c/2], [b/2 - c/2, 0]] r`Arrayrr@c3HK|]\}}|jr||k(ndyw)TN)r2)rrrs r4rz/TensExpr.replace_with_arrays..< s1?-7T4@Dt|$(@)?s "zshapes for tensor z expected to be z, replacement array shape is )rrrrcrrarrritemsr%rr1rJrLrrrr0rd _extract_datar)rOrrKrkremaprVrhrrexpected_shape index_type ret_indices last_indicess r4replace_with_arrayszTensExpr.replace_with_arrayssD !-RBFBWBWBYZQaggAFF1I:q8ZZ!'* 0HAu%&#/E>!&uGAJ"'-GAJ  0GWF\F\F^_]VUFE%L0__.335 "MFE&/26;Ah!?&**!?!?CICUCU!VZ*..!V!V>"ejjl23?;>~ <?<!7=~KK"!"" ""//0@A U"CCE7T_aqr e 5[` "@!Vs3F9(F>1GG cddlm}|j}|j}|Dcgc]'\}}||d||jj dz f)}}}|r ||g|}|Scc}}w)NrSumr`)sympy.concrete.summationsrrorIrTr1) rOr index_symbolsrrKrIrVr sum_indicess r4_check_add_SumzTensExpr._check_add_SumH s1""$hhdZdZ dZ!dZ"d#dZ#y)$ra Sum of tensors. Parameters ========== free_args : list of the free indices Attributes ========== ``args`` : tuple of addends ``rank`` : rank of the tensor ``free_args`` : list of the free indices in sorted order Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_heads, tensor_indices >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> a, b = tensor_indices('a,b', Lorentz) >>> p, q = tensor_heads('p,q', [Lorentz]) >>> t = p(a) + q(a); t p(a) + q(a) Examples with components data added to the tensor expression: >>> from sympy import symbols, diag >>> x, y, z, t = symbols("x y z t") >>> repl = {} >>> repl[Lorentz] = diag(1, -1, -1, -1) >>> repl[p(a)] = [1, 2, 3, 4] >>> repl[q(a)] = [x, y, z, t] The following are: 2**2 - 3**2 - 2**2 - 7**2 ==> -58 >>> expr = p(a) + q(a) >>> expr.replace_with_arrays(repl, [a]) [x + 1, y + 2, z + 3, t + 4] c|Dcgc]}|st|}}tj|}|jt|st j St|dk(r|dStj|g|i|Scc}w)NrFr`r) r!r_tensAdd_flattenrNrrrrLrr)rrrrDs r4rzTensAdd.__new__ sx%)/Q //''- & '66M t9>7N}}S3437330s B Bc"tjSrYrrVrns r4r z TensAdd.coeff uu r3c|SrYr2rns r4r_zTensAdd.nocoeff  r3c|jSrY) free_indicesrns r4rzTensAdd.get_free_indices s   r3c|jDcgc]%}t|tr|j|n|'}}|j|Scc}wrY)rrrrhr)rOrgr newargss r4rhzTensAdd._replace_indices sM_c_h_hiX[C1J3''-PSSiityy'""js*A cpt|jdtr|jdjSyrB)rrrrrns r4rz TensAdd.rank s+ diilH -99Q<$$ $r3crt|jdtr|jdjSgSrB)rrrrrns r4rzTensAdd.free_args s- diilH -99Q<)) )Ir3ct|jdtr|jdjSt SrB)rrrrrrns r4rzTensAdd.free_indices s2 diilH -99Q<002 25Lr3c <|jdd}|r*|jDcgc]}|jdi|}}n |j}|Dcgc]}|tjk7s|}}t |dk(rtjSt |dk(r|dSt j|t j|}d}|j||stjSt |dk(r|dS|j|}|Scc}wcc}w)NrETrr`ct|tsgggfSt|dr:t|dr.t|}|j|j |j fSgggfS)N_index_structurer\)rrrbget_index_structurer\rHrI)rrDs r4sort_keyzTensAdd.doit..sort_key s[a*2rz!q,-'!\2J'*||QVVQUU22r2: r3rFr2) r}rr$rrrLr_tensAdd_check_tensAdd_collect_termsrNr)rOhintsrEr rrrs r4r$z TensAdd.doit syy& 15;#HCHH%u%;D;99D $5saff}55 t9>66M Y!^7N t$--d3  h 66M t9>7Ndii G< 6sDD(Dcg}|D]N}t|ttfr%|jt |j >|j |P|Dcgc]}|js|}}|Scc}wrY)rrrrZrrrer )rrrDs r4rzTensAdd._tensAdd_flatten sj  A!c7^,aff&   (a(( )s A4,A4cdd}||d|ddDcgc] }|| }}tfd|Ds tdycc}w)Ncht|trt|jStSrY)rrrrrCs r4get_indices_setz/TensAdd._tensAdd_check..get_indices_set s'!X&1--/005Lr3rr`c3(K|] }|k( ywrYr2)rrDindices0s r4rz)TensAdd._tensAdd_check.. s7Q1=7sz&all tensors must have the same indices)rDrrzset[TensorIndex])rrd)rrr  list_indicesrs @r4rzTensAdd._tensAdd_check s[  #47+8 R RqkE*  25] SsCC -Cc|j}t||jr<|jDcgc] }t |}}t |j d}|St |Scc}w)zz Canonicalize using the Butler-Portugal algorithm for canonicalization under monoterm symmetries. FrD)expandrrrrPrr$)rOrrDrrs r4rPzTensAdd.canon_bp= sc {{} dDII &)-3AHQK3D34.%%5%1CJD> ! 4sA.ct|}t|tr+|jdk(rt d|j DSt|t r|j|jk7ryt|tr-t|j t|j k7ryy||z }t|t s|dk(St|tr|jdk(St d|j DS)Nrc3:K|]}|jdk(ywrNr rs r4rz!TensAdd.equals..M s7qww!|7FTc3:K|]}|jdk(ywrrrs r4rz!TensAdd.equals..] s8A177a<8r) r!rrr rrrrrr)rOrrs r4equalszTensAdd.equalsJ s eW %%++*:7TYY77 7 eX &yyEJJ& eW %499~UZZ0 5L!X&6M!W%ww!|#8888r3c~ttt5|j|cdddS#1swYyxYwrYr5r'r&rrOitems r4rzTensAdd.__getitem___ 1 4 5 #99T? # # #3<c|jDcgc]%}t|tr|j|n|'}}t |j d}t |Scc}wrC)rrrcontract_deltarr$rP)rOrrDrrs r4rzTensAdd.contract_deltad s[SWS\S\]a:a+B  'I]] TN  U  +{^s*Ac|jDcgc]}t||}}t|jd}t |Scc}w)a1 Raise or lower indices with the metric ``g``. Parameters ========== g : metric contract_all : if True, eliminate all ``g`` which are contracted Notes ===== see the ``TensorIndexType`` docstring for the contraction conventions FrD)rcontract_metricrr$rP)rOrrDrrs r4rzTensAdd.contract_metrici sH"04yy9!1%99 TN  U  +{:sAcg}|jD]2}t|tr|j|}|j |4t |j dSrC)rrrrjrerr$rOrkrr s r4rjzTensAdd.substitute_indices~ ]99 !C#x(,c,,l; OOC  !!&&E&22r3cg}|j}|D]}|jt|dj|}|j dd}|S)Nz + z+ -z- )rrer~rr")rOrrrDrs r4rzTensAdd._print sQ yy A HHSV   JJqM IIeT "r3c ddlm}m}t|jDcgc]'}t |t r|j|ng|f)c}\}}|Dcgc] }|| }}|d}tdt|D].}||} ||} tj| |} || | ||<0|t||j jfScc}wcc}w)Nr)rrr`)sympy.tensor.arrayrrrrrrrrrLrrxsumrr0) rOrrrr  args_indicesrrV ref_indicesrKrrs r4rzTensAdd._extract_data s9"@D % 9$>q$AB##A$6$6q$9:  ;tyy,',,%,88r3Nct|}|i}n|j}t|tsyt t |dk(r|j |||Sd}t|j|}t|j|}g}|dD]S}d} |dD]C} | |vr|j| ||} | d} |j| |j| n| rSy|dD cgc] } | |vs|  } } |dD]m}| D]f}|j|} | |j|||jvr||xx| j|z cc<|j| ho|dD cgc] } | |vs|  } } |dD]}| D]}|j|} | |j||j|jvr0||jxx| j|jz cc<|j| |dD cgc] } | |vs|  } } |d D]}| D]}|j|} | |j||j|jvr0||jxx| j|jz cc<|j| |dD cgc] } | |vs|  } } t | dkDry|Scc} wcc} wcc} wcc} w) Nrct|}t|t}t|dk(ryt|j vr)|dD] }t|j dk(s yyy)Nrnonwild WildTensorindexless_wildtensor wildtensor otherwild) _get_wildsr*typerLrkeysro)r  wildatomswildatom_typesws r4siftkeyz TensAdd.matches..siftkey si"3I!)T2N9~" ~2244' 56A1==?+q056$"r3rF)rrSTrrr)rrRrrrLrrr*rmatchesrzrerpopr])rOrrrSr query_sifted expr_siftedmatched_e_tensorsq_tensormatched_this_qe_tensorr@rremaining_e_tensorsres r4r zTensAdd.matches sFt}  I!(I$( z$ A %''i= = #DIIw/ 499g. $Y/ H"N' 2 00$$X$L9%)N$$Q'%,,X6 "! $+6i*@_QAM^D^q__k* (A( (IIaL=%,,Q/INN,,!! a0 $$Q'  ( (+6i*@_QAM^D^q__l+ (A( (IIaL=%,,Q/{{inn&66!!++.!%% 2DD.$$Q'  ( (+6i*@_QAM^D^q__45 (A( (IIaL=%,,Q/{{inn&66!!++.!%% 2DD.$$Q'  ( (+6i*@_QAM^D^q__ " #a ' E````s0/ K09K0; K5K5% K:/K: K?K?rr)rrr)$rrrrrrr r_rrhr(rrrr$rrrrrorarPrrrrrjrrrrrr4r rr r2r3r4rrq sM'R 4!#   &P   G G8  "9*#  *3 CAA  [[88  \\99 . " 9Tr3rcbeZdZUdZdZded<ded<dddZed Zed Z ed Z ed Z ed Z edZ edZedZedZedZedZedZedZdZedZd;dZdddZdZdZdZedZedZedZd Z d;d!Z!d"Z"d#Z#d$Z$dd,Z+d>d-Z,d.Z-d/Z.d0Z/d1Z0ed2Z1e1jdd3Z1e1jfd4Z1d5Z4d6Z5d7Z6d8Z7d9Z8d?d:Z9y+)@ra Base tensor class, i.e. this represents a tensor, the single unit to be put into an expression. Explanation =========== This object is usually created from a ``TensorHead``, by attaching indices to it. Indices preceded by a minus sign are considered contravariant, otherwise covariant. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead >>> Lorentz = TensorIndexType("Lorentz", dummy_name="L") >>> mu, nu = tensor_indices('mu nu', Lorentz) >>> A = TensorHead("A", [Lorentz, Lorentz]) >>> A(mu, -nu) A(mu, -nu) >>> A(mu, -mu) A(L_0, -L_0) It is also possible to use symbols instead of inidices (appropriate indices are then generated automatically). >>> from sympy import Symbol >>> x = Symbol('x') >>> A(x, mu) A(x, mu) >>> A(x, -x) A(L_0, -L_0) Fr>rztuple[TensorHead, Tuple]rrc 6|j||}tj||t|fi|}t j ||_|j jdd|_|j jdd|_ |j j|_ tj|_||_||_|g|_|j$t'|k7r t)d||_t,j/||j |_|S)Nwrong number of indices)_parse_indicesrrrr>rWrrH_freerI_dumrMrrV_coeff_nocoeff _component _componentsrrLrdrr_build_index_map _index_map)r tensor_headrKrrrs r4rzTensor.__new__F s$$[':mmCeWoII.;;WE((--a0 ''++A.,,66 UU  $&-   s7| +67 7%00#:N:NO r3c|jSrYr/rns r4rHz Tensor.freeW rpr3c|jSrYr0rns r4rIz Tensor.dum[ s yyr3c|jSrYrMrns r4rzTensor.ext_rank_ s ~~r3c|jSrYr1rns r4r z Tensor.coeffc s {{r3c|jSrY)r2rns r4r_zTensor.nocoeffg s }}r3c|jSrY)r3rns r4r]zTensor.componentk s r3c|jSrY)r4rns r4r\zTensor.componentso sr3c |jdSrBrrns r4headz Tensor.heads rr3c |jdSrrrns r4rKzTensor.indicesw rr3cHt|jjSrY)rrrrns r4rzTensor.free_indices{ s4((99;<)>)B)BCD "+DIIaL"9L3Q)^LLLs A A cFt|jdjSr)rrrns r4rzTensor._get_indices_set s499Q<$$%%r3cR|jDcgc] \}}||df c}}Scc}}wrBrH)rOrrls r4 free_in_argszTensor.free_in_args s$.2ii8(#sc1 888s#cT|jDcgc] \}}||ddf c}}Scc}}wrB)rI)rOp1p2s r4 dum_in_argszTensor.dum_in_args s&-1XX662rRA666s$cXt|jDcgc]}|d c}Scc}wrBrrs r4rzTensor.free_args #TYY/qt/00/ 'ct|tsyt|tr%|jj |jSt S)z :param other: :return: 0 commute 1 anticommute None neither commute nor anticommute r)rrrr]rrrs r4rzTensor.commutes_with s:%* v &>>//@ @""r3ct|||S)z Returns the tensor corresponding to the permutation ``g``. For further details, see the method in ``TIDS`` with the same name. rrOrrs r4rzTensor.perm2tensor s 4K00r3c|jr|S|j}|jj\}}}t |j g}t |||g|}|dk(rtjS|j|d}|S)NrT) rrrrrr]rrrr)rOrrrrrcanrs r4rPzTensor.canon_bp s   K{{}00CCE7D !4>>"2 31gt0a0 !866M!!#t, r3c|gSrYr2rns r4rz Tensor.split s v r3c|SrYr2rns r4sorted_componentszTensor.sorted_components s r3c2t|jdS)zN Get a list of indices, corresponding to those of the tensor. r`rrns r4rozTensor.get_indices sDIIaL!!r3c6|jjS)zS Get a list of free indices, corresponding to those of the tensor. rrrns r4rzTensor.get_free_indices s$$5577r3c$|j|SrYxreplacerfs r4rhzTensor._replace_indices s}}T""r3c&|tjfSrYrrns r4 as_base_expzTensor.as_base_exp sQUU{r3c`g}|jD]}|D]w\}}|j|jk(s |j|jk(s:|j|jk(r|j |n|j | ~|j ||j |S)a Return a tensor with free indices substituted according to ``index_tuples``. ``index_types`` list of tuples ``(old_index, new_index)``. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> A, B = tensor_heads('A,B', [Lorentz]*2, TensorSymmetry.fully_symmetric(2)) >>> t = A(i, k)*B(-k, -j); t A(i, L_0)*B(-L_0, -j) >>> t.substitute_indices((i, k),(-j, l)) A(k, L_0)*B(-L_0, l) )rKrbrTrcrerD)rOrkrKrhind_oldind_news r4rjzTensor.substitute_indices s$\\ &E$0 & JJ',,.53J3J3:3L3L4M{{gmm3w/x0 &u% &tyy'""r3c |j}|jj}|j}d}|h}||k7r|j }|D]}|D]}|j }t d|D cgc]} |j| } } d|j||z z} tt|| D cgc]} ||  c} } |j| |j| z||k7r|Scc} wcc} w)zl Return a list giving all possible permutations of self that are allowed by its symmetries. Nrr) r]rrrrRrorapplydictraddrh) rOcompgensr old_perms new_permsr'genindsrVpersignind_maps r4_get_symmetrized_formszTensor._get_symmetrized_forms s~~}}''yy F 9$!(I! KKC++-D16q?A399Q<?C?#))D/D"89D"3ts-C!d1g-C#DEGMM4$*?*?*H#HJ K K9$ @-Cs 1C71 C<Nc(t|}|i}n|j}||k(r|St|tsy|j|jk7ry|j D]-}|j |||}||j||cSy)NrS)rrRrrrDr_matchesrz)rOrrrSnew_exprr@s r4r zTensor.matches5 st}  I!(I 4< $' 99 !335 !H h s ;A}  #   ! r3ct|}|i}n|j}||k(r|St|tsy|j|jk7ry|j }|j }t |t |k7rytt |D]R}||}|j||}|y| |jvr||  ||k7ry|j|T|S)zD This does not account for index symmetries of expr N) rrRrrrDrorLrr rrz) rOrrrS s_indices e_indicesrVs_indr@s r4rzTensor._matchesN st}  I!(I 4< $' 99 !$$& $$& y>S^ +s9~& $AaLE il+Ay9>>++E61B0Bah0N  # $r3ct|j}t|}|Dcgc]}|jc}|Dcgc]}|jc}k7r t d||k(r|S|j tt ||}t|Dchc]}|jr|n| c}t|k7r|j|jS|Scc}wcc}wcc}wNr r<rrrTrdrjrrLrcrrrOrKrrDrrVs r4razTensor.__call__s NN w-)0 1AA   1S\5]aa6I6I5] ]12 2 i K #D # #T#i*A%B C g6QWW1"$6 73w< G166166? " 25]7CCC$cttt5|jj cdddS#1swYyxYwrYr3rns r4r4zTensor.__iter__ r5r6c~ttt5|j|cdddS#1swYyxYwrYrrs r4rzTensor.__getitem__ rrc Tddlm}|jD];\}}t|ts|j d|j dk(s7|}|}nt |d||jDcic]\}}|||}}}||}|j}|j}t|dkDr-|D]} | |vstd|z|D cgc] } | |vs|  }} t|dkDr|j} |j} i} |D]\} }| |  | | |<| |fD]u}| |j| |jz s#|| |j}| |jrtj|}|j|||t| }w|j!| j#}tj%|g|t| }|j'}|j'}|j)||||Scc}}wcc} w)Nr`rrz not found in z'%s with contractions is not implemented)rrrrrrrdrIrLrrorcrTrrLrryr$rrr)rOrrrrrrdum1dum2pairrvrwrgrerfrlrrrs r4rzTensor._extract_data sE $**, NDAq!V$diil)B  N T;KLM M5E4J4J4LMDAqAuQxKMMe xxyy t9q= at#-.WZ_._`` a &*>TT-=D>D> t9q='')H((*HD jB&.rl]Xb\"8jC}**Xc]-@-@@!1(3-2Q2Q!R#C=..%=%L%LV%TF $ F FvuVY[^_g[h i j jNN4(--/E,>>wcRZm\E))+ **, 44UIyRbcc?N?s8H H%#H%crttt5t|cdddS#1swYyxYwrYrrns r4rz Tensor.data rrcrttt5|t|<dddy#1swYyxYwrYrr.s r4rz Tensor.data s1 4 5 837 *4 0 8 8 8r0cttt5|tvrt|=|jtvrt|j=dddy#1swYyxYwrYrrns r4rz Tensor.data rrc|jDcgc] }t|}}|j}|jdkDr!|jddj |dSd|jzScc}w)Nrrz, rz%s)rKr~r]rrbr)rOrrKr]s r4rz Tensor._print s^'+||43s844NN >>A  ) '0BC D9>>) * 5sA.c|dk(r|jdk(St|}t|ts!|jrJt j |k(Sd}||||k(S)Nrc|j}|jt|jtt |j tt |j f}|SrY)rPr rr\rrHrI)rOrrs r4_get_compar_compz'Tensor.equals.._get_compar_comp sK A% -&.)5+?AAHr3)r r!rrr\rrV)rOrrs r4rz Tensor.equals sb A:::? "%* &&55E> !   %)9%)@@@r3c|j|k7r|St|jdk7r|S|jtj dk(rd}nP|jtj dk(rd}n+|jtj dk(rd}nttj}|jd}|s||jz}|S||jz}|jd\}}||kr| }|S)Nrrr`r@) r]rLrHrrrrrrrVrJr1rI)rOrantisymrridp0dp1s r4rzTensor.contract_metric s >>Q K tyy>Q K ::77; ;G ZZ>99!< <G ZZ>55a8 8G% %uummA?q? ?tyy|4m4""477@sAct|tstjS|j|jk7rtjSdg}t t |j|jD]\}\}}|j|jk7r td|j}|j}tdt|z||j}|j|jk(r|j|| } n%t||||j| | } |j!| tj#|j%dS)Nr`zindex types not compatibled_r FrD)rrrrrDrarrrTrdrrrr~rcrrrer r$) rOrkronecker_delta_listcountiselfiotherrT tensor_metricdummykroneckerdeltas r4rzTensor._eval_partial_derivative sN!V$66MyyAFF"vv %&3 -6c$:O:O:QSTSeSeSg6h,i @(**f.F.FF$%ABB(-(?(?%$5$<$>E>J Jr3rrrr)rrrr):rrrrr rcrrrHrIrr r_r]r\rDrKrrJrrr5r$r.rVrSr{rrrcrgrrrrPrrsrorrhr{rjrr rrar4rrrrrrrrrr rr2r3r4rr s]!FN%% "":?"  ==%%&& "D27P :M&997711 #1 " 8 # #@,2#J"( # *dX88  [[88  \\@@+A D,8)Kr3rceZdZUdZej Zded<dZe dZ e dZ e dZ e dZ e d Ze d Zed Zed Zed@d ZedZedZdZedZedZdZdZdZe dZe dZe dZe dZe dZ e dZ!dZ"dZ#dAdZ$dBdZ%d Z&d!Z'd"Z(d#Z)d$Z*d%Z+d&Z,dCd(Z-d)Z.d*Z/d+Z0d,Z1dCd-Z2d'd.d/Z3ed0Z4d1Z5d2Z6d3Z7e d4Z8e8jrd5Z8e8jtd6Z8d7Z;ed8Zd;Z?d<Z@dDd>ZAdDd?ZBy=)Era Product of tensors. Parameters ========== coeff : SymPy coefficient of the tensor args Attributes ========== ``components`` : list of ``TensorHead`` of the component tensors ``types`` : list of nonrepeated ``TensorIndexType`` ``free`` : list of ``(ind, ipos, icomp)``, see Notes ``dum`` : list of ``(ipos1, ipos2, icomp1, icomp2)``, see Notes ``ext_rank`` : rank of the tensor counting the dummy indices ``rank`` : rank of the tensor ``coeff`` : SymPy coefficient of the tensor ``free_args`` : list of the free indices in sorted order ``is_canon_bp`` : ``True`` if the tensor in in canonical form Notes ===== ``args[0]`` list of ``TensorHead`` of the component tensors. ``args[1]`` list of ``(ind, ipos, icomp)`` where ``ind`` is a free index, ``ipos`` is the slot position of ``ind`` in the ``icomp``-th component tensor. ``args[2]`` list of tuples representing dummy indices. ``(ipos1, ipos2, icomp1, icomp2)`` indicates that the contravariant dummy index is the ``ipos1``-th slot position in the ``icomp1``-th component tensor; the corresponding covariant index is in the ``ipos2`` slot position in the ``icomp2``-th component tensor. r>rc v|jdd}ttt|} |Dcgc] }t |}}t t j|}g}|D]}t t|}|Dcgc] }t|} }t t j| } t t |} t|j|dkDr)| j|| } tj|| } n|} |j| |}|D cgc].}t|tt fr |j"n|gD]} | 0}}} tj%|d\}}}}|D cgc]} | j&}} t)|||||}t+j,|g|}||_|j1|_||_|j6dd|_|j:dd|_|j6Dchc]}|d c}|_t|j6|_ t|j4j6dt|j4j:zz|_!tDjF|_$||_%|Scc}wcc}wcc} }wcc} wcc}w)NrFr)replace_indicesrPr@)&r}rrr!rrr\r]get_dummy_indicesrL intersectionunionr_dedupe_indicesrerrrrMrTr>rr_indicesrR _index_typesrrHr/rIr0 _free_indices_rankrMrrVr1 _is_canon_bp)rrrrr rHrdum_thisr dum_other free_thisexcludenewargrVrKrIrJrJrrDs r4rzTensMul.__new__m sxkk-7 C$'( 266# %669??D)* #C,S12H7>?!*1-?I?IOOY78I,S12I8((./!3#//$ : 00g> NN6 " #"dc 3RU8WCHH^a]bdaddd#*#D#DT[`#D#a gtS5< s (9(9r3c|jSrYr9rns r4rEzTensMul. r3c|jSrYr;rns r4rEzTensMul. s  r3c|jSrYrrns r4rEzTensMul. s ););r3c|jSrY)rrns r4rEzTensMul. rr3c|jSrYr=rns r4rEzTensMul. s T^^r3c i}i}g}g}d}t|D]\}}|D]}t|ts td| |vrp|j | } |j | } |j r|j ||| || fn|j | | || |f|j |n-||vrtd|z|||<|||<|j ||dz }t|j} |jD cgc]} | j} } |jd|| | |fScc} w)Nrzexpected TensorIndexzRepeated index: %sr`c |dSrr2rCs r4rEz.TensMul._indices_to_free_dum.. s adr3rF) rarrrrr!rcrerdrrrrbrN)r free2pos1 free2pos2 dummy_datarKrvru arg_indicesrh other_pos1 other_pos2rHrV free_namess r4_indices_to_free_dumzTensMul._indices_to_free_dum sb   !*.dummy_name_gen rr3r@) rorrr rrTrrrrrrhr)rrrz replacementsr rrKrHrrr old_indexpos1cov pos1contrapos2cov pos2contrarrrrgrIrs @r4rMz!TensMul._tensMul_contract_indices s$()q) )599S C(9 9070L0L\0Z-z:# ; GQ -C 7J&88 !/ !;J!3' z:S qr@RS38 W%i08=v Z()4#( ',f # -"%Ts E E-EEcg}|D]?}t|tst|tr%|j|jA|S)zq Get a list of ``Tensor`` objects having the same ``TIDS`` if multiplied by one another. )rrrrZr\)rr\r s r4_get_components_from_argsz!TensMul._get_components_from_argssL   .Cc8,#w'   cnn -  . r3c|j}d}t|D]C\}}t|ts|}||jz }t |j |||||<EyrB)rorarrrrr])rrJrKind_posrVr prev_poss r4_rebuild_tensors_listzTensMul._rebuild_tensors_listsi!--/o GFAsc8,H s|| #GS]]GHW,EFDG  Gr3c |j}|jdd}|rw|jDcgc]}|jdi|}} t t |j|}|j |}|jDcgc]}|| }}n |j}|Dcgc]}||jk7s|}}td|Dcgc]}t|tr|c}tj}|Dcgc]}t|ts|}}t|dk(r|S||jk7r|g|z}|dk(rtjSt|dk(r|dStj!|\}} } } | D cgc]} | j"} } t%| | | | |}|j&|}| |_||_t|j*j,dt|j*j.zz|_||_||_|Scc}wcc}wcc}wcc}wcc}wcc} w) NrETc ||zSrYr2rbs r4rEzTensMul.doit..1s AaCr3rr`rr@r2)rr}rr$rr_dedupe_indices_in_ruleidentityrrrrrVrLrrrMrTr>rrrrHrIrMr1)rOrrrEr rrulerr rKrHrIrVrJrJrs r4r$z TensMul.doits'' yy& 15;#HCHH%u%;D;  DIIt,-D//5D%)YY/DG/D/99D#<sdmm';<<')_#ZPSU]E^#)_abafafg#Az#x'@AA t9>L DMM !7T>D A:66M t9>7N#*#D#DT#J gtS5<uowLLZY]_bcognottztA Ar3ctj|||}|j}|Dcgc]}d}}d}t|D](\}}|} ||jz }t ||| |||<*|Scc}w)zv Get a list of ``Tensor`` objects by distributing ``free`` and ``dum`` indices on the ``components``. Nr)r>r^rorarr) r\rHrIrJrKrVtensorsrr]rs r4rz-TensMul._get_tensors_from_components_free_dumSs *BB:tUXY!--/!+,A4,,%j1 FLAyH y~~ %G 78G+DEGAJ F-s A1cF|jDchc]}|d c}Scc}wrBrbrns r4r{zTensMul._get_free_indices_setds"ii(!(((s cttj|j}t |j j Dchc] \}}||vs |c}}Scc}}wrY)rr\r]rIrarror^s r4rzTensMul._get_dummy_indices_setgsN 23 "+D,A,A,M,M,O"Pc3TUYbTbcccs A!A!ct|jDcgc]}d}}d}|jD]D}t|tst|jD] }||||z< ||jz }F|Scc}wrB)rrrrr)rOrV arg_offsetcounterr rs r4 _get_position_offset_for_indicesz(TensMul._get_position_offset_for_indicesks$)$--$89qd9 999 $Cc8,3<<( 2*1 1w;' 2 s|| #G  $ :s A:cXt|jDcgc]}|d c}Scc}wrBrrs r4rzTensMul.free_argsvrirjc8|j|jSrY)rrrns r4r\zTensMul.componentszs--dii88r3c|j}|j}|jDcgc]\}}||||z ||fc}}Scc}}wrY)r_get_indices_to_args_posrH)rOrargposrrls r4rczTensMul.free_in_args~sN::< ..0JN))TJS#c*S/)6#;7TTTsA c|jSrYr?rns r4r z TensMul.coeffs{{r3cx|jg|jd|jz jdSNr`FrD)rrr r$rns r4r_zTensMul.nocoeffs5tyy2$))2Qtzz\277U7CCr3c |j}|j}|jDcgc]\}}|||z |||z ||||fc}}Scc}}wrY)rrrI)rOrrrerfs r4rgzTensMul.dum_in_argssb::< ..0]a]e]efSYSUWYJrN"Bz"~$5vbz6":Nfffs"Ac|dk(r|jdk(St|}t|ts|jrJ|j|k(S|j |j k(SrB)r r!rrr\rPrs r4rzTensMul.equalss^ A:::? "%* &&::& &}}%.."222r3c|jS)a+ Returns the list of indices of the tensor. Explanation =========== The indices are listed in the order in which they appear in the component tensors. The dummy indices are given a name which does not collide with the names of the free indices. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensor_heads('p,q', [Lorentz]) >>> t = p(m1)*g(m0,m2) >>> t.get_indices() [m1, m0, m2] >>> t2 = p(m1)*g(-m1, m2) >>> t2.get_indices() [L_0, -L_0, m2] )rrns r4rozTensMul.get_indicess6}}r3c6|jjS)a Returns the list of free indices of the tensor. Explanation =========== The indices are listed in the order in which they appear in the component tensors. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensor_heads('p,q', [Lorentz]) >>> t = p(m1)*g(m0,m2) >>> t.get_free_indices() [m1, m0, m2] >>> t2 = p(m1)*g(-m1, m2) >>> t2.get_free_indices() [m2] rvrns r4rzTensMul.get_free_indicess2$$5577r3c |j|jDcgc]%}t|tr|j |n|'c}Scc}wrY)rrrrrh)rOrgr s r4rhzTensMul._replace_indicessCtyygkgpgpq`cC9R3//5X[[qrrqs*A c|jdk(r|gSg}d}|jD].}t|tr|j||zd}*||z}0|S)a Returns a list of tensors, whose product is ``self``. Explanation =========== Dummy indices contracted among different tensor components become free indices with the same name as the one used to represent the dummy indices. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz) >>> A, B = tensor_heads('A,B', [Lorentz]*2, TensorSymmetry.fully_symmetric(2)) >>> t = A(a,b)*B(-b,c) >>> t A(a, L_0)*B(-L_0, c) >>> t.split() [A(a, L_0), B(-L_0, c)] r2r`)rrrre)rOsplitprr s r4rz TensMul.splitsb0 99?6M99 C#v& c#g&s     r3c |jDcgc]'}t|ttfr |jn|f)}}tt j |Dcgc]}t |jdc}Scc}wcc}wrC)rrrrr\productrr$)rOrr args1rVs r4_eval_expand_mulzTensMul._eval_expand_muls~TXT]T]^SZc7^<3&H^^2;2C2CU2KM-.GQK  %  (M  _Ms ,A9A>cnttj||jj dS)Nr+FrD)rrr=rr$rns r4rzTensMul.__neg__s*q}}d8I8IJOOUZO[[r3c~ttt5|j|cdddS#1swYyxYwrYrrs r4rzTensMul.__getitem__rrct|j}|jjr.d}|dtj k(r |dd}||fS|d |d<||fSd}||fS)Nrrr`r)rrr could_extract_minus_signrr=)rOrrs r4!_get_args_for_traditional_printerz)TensMul._get_args_for_traditional_printer syDII :: . . 0DAw!--'ABx Tz 7(QTzDTzr3c |jDcgc]}t|ts|}}d}t|dz }t |D]}t ||dD]}||dz j ||}|dvr"t t||dz jjd}t t||jjd} |||dz jjf| ||jjfkDs||||dz c||dz <||<|s| }||jz} | dk7r| g|zS|Scc}w)z Returns the ``args`` sorted according to the components commutation properties. Explanation =========== The sorting is done taking into account the commutation group of the component tensors. r`rrc|jSrYrrCs r4rEz:TensMul._sort_args_for_sorted_components..+s PQPVPVr3rFc|jSrYrrCs r4rEz:TensMul._sort_args_for_sorted_components..,s affr3) rrrrLrrrrr]rJrbr ) rOr cvrrfrVrrvrwtyp2r s r4 _sort_args_for_sorted_componentsz(TensMul._sort_args_for_sorted_componentss["YY Dc*S(*Cc D D GaKq %A1a_ %qsG))"Q%0F?c"QqS'"3"3"?"?@FVWc"Q%//"="=>DTU"QqS'++001T2a5??;O;O4PP%'UBqsGNBqsGRU $u % %tzz! A:7R<  )Es EEcNt|jjdS)zB Returns a tensor product with sorted components. FrD)rr!r$rns r4rszTensMul.sorted_components8s&==?@EE5EQQr3Fct|||S)z Returns the tensor corresponding to the permutation ``g`` For further details, see the method in ``TIDS`` with the same name. r+rmrns r4rzTensMul.perm2tensor>s 4 <>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> A = TensorHead('A', [Lorentz]*2, TensorSymmetry.fully_symmetric(-2)) >>> t = A(m0,-m1)*A(m1,-m0) >>> t.canon_bp() -A(L_0, L_1)*A(-L_0, -L_1) >>> t = A(m0,-m1)*A(m1,-m2)*A(m2,-m0) >>> t.canon_bp() 0 FrDrT)rrrrrPr\r$rsrrrrrrr) rOrrrrrrrptmuls r4rPzTensMul.canon_bpFs&   K{{} dG $==? "Kyyey$  " " $--@@B7D !!,, /1gt0a0 !866M}}S$' r3c(|j|}|SrYr)rOrrs r4rzTensMul.contract_deltajs   'r3ci}d}t|jD]L\}}t|tst|tsJt |j D] }|||<|dz }N|S)zU Get a dict mapping the index position to TensMul's argument number. rr`)rarrrrrr)rOpos_map pos_counterarg_ir rVs r4rz TensMul._get_indices_to_args_posnsz #DII. !JE3c8,c6* **3<<( !', $q  !  !r3c  |jjd}||k7rt|}t||S|j }t |j }|jtjdk(rd}nP|jtjdk(rd}n+|jtjdk(rd}ntt|j Dcgc](\}}t|ts|j|k(s'|*}}}|s|Sd} |j dd} |j"dd} t%} |D]} | | vr | Dcgc]}||d| k(s|}}| Dcgc]}||d| k(s ||d| k(s|}}|sM| j'| d|| <t)|dk(r|sF|\}}||d| k(r|d}n|d}||d| k(r|d}n|d}| j+||fn3|\}}||d| k(r|d}|ddk(r| } n|d}|ddk(r| } ||d| k(r |d}| } n|d}| j+||fnt)|dk(r|s[|d\}}||||k(r|j,d}| |j.z} n||| k(r|}n|}|d\}}| j+||fnj|d\}}||||k(r'|j,d}| |j.z} ||kr4| } n0||| k(r |}|dk(r| } n|}|d\}}| j+||f| Dcgc] }||vs| } }| Dcgc] }||vs| } }d}dgt)|z}t1t)|D]}|| vr|dz } |||<| Dcgc]\}}||| vs|||||z f} }}| Dcgc]+\}}||| vs||| vs||||z ||||z f-} }}| t3|zjd}t|t4s|St6j9|j:| | }|j=|Scc}}wcc}wcc}wcc}wcc}wcc}}wcc}}w)a Raise or lower indices with the metric ``g``. Parameters ========== g : metric Notes ===== See the ``TensorIndexType`` docstring for the contraction conventions. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz) >>> g = Lorentz.metric >>> p, q = tensor_heads('p,q', [Lorentz]) >>> t = p(m0)*q(m1)*g(-m0, -m1) >>> t.canon_bp() metric(L_0, L_1)*p(-L_0)*q(-L_1) >>> t.contract_metric(g).canon_bp() p(L_0)*q(-L_0) FrDrr`r@rN)rr$rPrrrrrrrrrrarrr]rIrHrrrLrerJr1rrrr>r^r\rV)rOrrr(rrrVrDgposrrIrHelimgposxfree1rdum10dum11p0rerrrirrshiftshiftsrrUs r4rzTensMul.contract_metric}sD8{{}!!u!- 4<D>D"4+ +//1DII ::77; ;G ZZ>99!< <G ZZ>55a8 8G% %( 2adajF6KPQP[P[_`P`aaKhhqkyy|uY 7E} $?1! (>Q?E?"W!gadmu&<! QV@VAWDW HHUODK4yA~#'LE5uQx(E1"1X#1XuQx(E1"1X#1XJJBx(#'LE5uQx(E1"1X 8q=$(5D#1X 8q=$(5DuQx(E1"1X $u#1XJJBx(Ta#AwHCs|ws|3mmA.#CGG|#3<50!$B!$B!&qQ S"I.#AwHCs|ws|3mmA.#CGG|9$(5D#3<50!$B"ax(,u!$B!&qQ S"I.!3Qd]13C3#6!q~A6D6sY 7xSYs4y! ADy F1I   ?C](3gajX\F\a&,,-]]RUNBY`acYdlpYpu|}vAIMvMVGBK(("vgbk/B*BCNNWd^#***6#x(J  5 5cnndC P++B//ib@Wh46^Ns`/QQQQ#-Q#7Q(Q(? Q- Q- Q2Q2$Q74Q7 Q=Q=#Q=cF|j}|j|d|iSrQrRrTs r4rVz TensMul._set_new_index_structure!rWr3r+cjt||jk7r tdt|j}d}t |D]Q\}}t |tst |tsJ|j}|j||||z||<||z }St|d|ijdS)NrYrrFrD) rLrrdrrrarrrrSrr$) rOrrKrrrlrVr rs r4rSzTensMul._set_indices%s w<4== (67 7DIIo FAsc8,c6* **||H&c&&CL(ABDG 8OC  6+6;;;GGr3cX|D]%}t|tst|tr%JyrY)rrr)rrHrIr s r4&_index_replacement_for_contract_metricz.TensMul._index_replacement_for_contract_metric3s. +Cc8,c6* ** +r3cg}|jD]2}t|tr|j|}|j |4t |j dSrC)rrrrjrerr$rs r4rjzTensMul.substitute_indices:rr3ct|j}t|}|Dcgc]}|jc}|Dcgc]}|jc}k7r t d||k(r|S|j tt ||}t|Dchc]}|jr|n| c}t|k7r|j|jS|Scc}wcc}wcc}wrrrs r4razTensMul.__call__Brrc Bt|jDcgc]$}t|ts|j |&c}\}}t t j|jDcgc]}t|tr|c}tj}tj|\}}} } tj| } |j} |jd|D cgc]} | d }} ||tj!|| | zfScc}wcc}wcc} w)Nc |dSrr2rCs r4rEz'TensMul._extract_data..Xs !r3rFr)rrrrrroperatormulrrVrrrrrNrr)rOrr rrrr rKrHrrrIrrVrs r4rzTensMul._extract_dataRs"TXT]T]${SakloqyazS%6%67G%H${| fx||%ZA*QPXBYa%Z\]\a\ab070L0L\0Z-z:((4== n %&*+!+ +U#;#M#MfVY[c#dddd%|%Z ,sDD&D <D ( Dcttt5t|j }ddd|S#1swYSxYwrYr)rOrs r4rz TensMul.data\s> 4 5 @0?C @  @ s <Ac,ttd)Nz9Not possible to set component data to a tensor expressionr5rdr.s r4rz TensMul.datacsTUUr3c,ttd)Nzt|}tt|}tt|}|j|}t |dk(ry |j ||j ||Dcgc]}|df}}t j|}i}|D]T} | |jvr|| j} | j| g| jdd} | || <| || <Vt |dk(ry|j|} | Scc}w)z exclude: set new: TensExpr If ``new`` has any dummy indices that are in ``exclude``, return a version of new with those indices replaced. If no replacements are needed, return None rNr`) rrrrrLrzr>rqrrTrrrh) rTrdums_newfree_new conflictsrVexclude_for_genrrgdnewnamenew_d new_renameds r4rzTensMul._dedupe_indicests)g,(-.',-))'2 y>Q  x x .56At966>>O ArTYY[ !--.GAFF70QVVABZ0EDGvD!H   t9>**40 7s; Dcz|jDcic]\}}t|ts||}}}|jDcic]\}}||jvs||}}}t |j }i}|j ||j |j|j |j|jD]G\}} tj| |} || k(s| | ||<)| ||<|j t | I|Scc}}wcc}}w)zb rule: dict This applies TensMul._dedupe_indices on all values of rule. ) rrrrrrrorzvaluesrr) rOrrr index_rules other_rulesrnewrulerSrTrLs r4rzTensMul._dedupe_indices_in_rules'+jjlQsqjK6PqsQ Q&*jjlRsqa{?O?O?Q6QqsR Rd&&(){#{'')*{))+,#))+ 9HC!11#w?Kcz[0" * {;78  9RRsD1D1D7#D7ct|tsy|h}|j||}|y|j||j dS)zx If new is an index which is already present in self as a dummy, the dummies in self should be renamed. NFrD)rrrr_subsr$)rOrSrTr self_renameds r4 _eval_subszTensMul._eval_subssT #{+%++D':  %%c3/44%4@ @r3cddlm}|jDcgc]}|jd}}|Dcgc]}t ||r|jdn|!}}t j |}|j||Scc}wcc}w)Nrr)rrrorrrr r)rOrrrrVrr rs r4r z TensMul._eval_rewrite_as_Indexeds|1,0,<,<,>?q? ?HLMz#s3 <MM||D!""477@Ms B$Bc g}t|jD]\}}t|tr|j |}n.|j r|j |}ntj}|sY|jtj|jd||fz|j|dzdzjdtj|jdSr )rarrrrrrrrrerr r$r)rOrtermsrVr rIs r4rz TensMul._eval_partial_derivatives * jFAs#x(003;;,,Q/AA W--diimqd.BTYYqSTuvEV.VW\\bg\hi j&+++77r3Nc  $%t|ts#t|ttfr t|}ny|i}nT|j }|j Dcgc]}t|t s|c}%%fd}||}||}td|jDcgc]}t|tr|c}tj}||k(r|Stt|dk(r|j|||Sd}t|j|} t|j|} d| jvr9t| dj!d|j"k7rt%d | dd| jvr/t| dj!d|k7rt%d | d| d D chc]} t't)| d g} } | d D chc]} t't)| d g} } | j+| syt| d dkDrx| d d} |j-D]Z}| d D]N}t|trd }nd } ||zj/|}|1|j0|g|jDcgc] }||k7s | c}j!d}|j0|jDcgc] }||k7s | c}j!d}i}|j3||j3||j5||}|d}|jD]8}t|t s| |jvs(|| || k7s6d}n|s:|j3||ccS]yg}| d }t| ddd\}}|D]}t7|j9}g}|D]F} | j9}!d}"|!D]$t;$fd|Dsd}"|"s6|j=| H|j5t|j!d}|y|D]}#|j=|#|j3|| d D cgc] } | |vs|  }} t|dkDrC|dj5td g|j!d}|y|j3|nt|dkDry|j"j5||}|y|j3||Scc}wcc}wcc} wcc} wcc}wcc}wcc} w)z Match assuming all tensors commute. But note that we are not assuming anything about their symmetry under index permutations. Nc<tj|}||S|SrY)rr)rrenamedrKs r4dedupez,TensMul._matches_commutative..dedupes%!11$@&"NKr3c ||zSrYr2rs r4rEz.TensMul._matches_commutative..s 1r3rcTt|tryt|ttfryy)Nrrr )rrrr)r s r4rz-TensMul._matches_commutative..siftkey s$#z*#C&(!34r3r FrDz/Found something that we do not know to handle: rr\rr`)rTrc:t|jdk(SrB)rLrrCs r4rEz.TensMul._matches_commutative..NsCPQPbPbPdLeijLjr3)binaryc3DK|]}|jduywrY)r )rrrVs r4rz/TensMul._matches_commutative..WsHA199Q<4/Hs r)rrrrrRrNrrrrrrVrLrrr*rr$r rrgetattrissubsetrrrrzr rrrre)&rOrrrSrr\r  expr_coeffrr"r#rDquery_tens_headsexpr_tens_headsr%q_tensr)rr@r rem_queryrem_exprtmp_replrem_minternally_consistentr$r(indexless_wildswildsrfree_this_wildtensors_to_tryrrHshares_indices_with_wildr'rVrKs& @@r4_matches_commutativezTensMul._matches_commutatives $($4 01t}  I!(I#,"2"2"4SQ 1k8RqSG $*L),[\gho\p[q*rssIUV^I_`AE'!\2">?``HST\H]^15L"!=>^^((9 |H% & *#H-a0H #99; -$X.-A!&'2! f..q1Ay ) $ YTYY1X!x-!1X Y ^ ^di ^ jI(tyydii*J161*JKPPV[P\H!HOOI.OOA&%--h(-KE(04-!&*A)!K8$%2#5%)ay:P277U7CEAy*3D%,,T23  #' $,+6h*?^Q1L]C]q^^  ! # #++WQ-L8K-L-Q-QW\-Q-]_Ay  # $ % ) JJ  zs  3 9   Q KT/j8a^42Y*J\_sHT/3T/$T4 :T4 T9-T> U*U U )U U U ct|}|i}n|j}td|jD}|r|j |||St d)Nc3pK|].}t|ts|jjdk(0ywr)rrr]r)rr s r4rz"TensMul.matches..s*^#jQTV\F]cmm((A-^s66z9Tensor matching not implemented for non-commuting tensors)rrRrrrrr)rOrrrScommutes r4r zTensMul.matcheszsZt}  I!(I^^^ ,,T9cB B%&ab br3rrrrr)CrrrrrrVrrcrrrJrHrIrrrrrrrMrrr$rrr{rrrr\rcr r_rgrrorrhrrrrrr!rsrrPrrrrVrSr8rjrarrrrr4rrrUr rrrr r2r3r4rrB s%LuuH%%-^9:K + ,D ) *C;>> from sympy.tensor.tensor import TensorIndexType, TensorHead, TensorSymmetry >>> from sympy import symbols >>> L = TensorIndexType("L") >>> i, j, k = symbols("i j k") >>> A = TensorHead("A", [L, L], TensorSymmetry.fully_symmetric(2)) >>> A(i, j).get_free_indices() [i, j] If we want to set component ``i`` to a specific value, use the ``TensorElement`` class: >>> from sympy.tensor.tensor import TensorElement >>> te = TensorElement(A(i, j), {i: 2}) As index ``i`` has been accessed (``{i: 2}`` is the evaluation of its 3rd element), the free indices will only contain ``j``: >>> te.get_free_indices() [j] c t|tsOt|tstd|z|j|j Dcgc]}t ||c}S|j}|Dcic]}|j d|}}|jDcic]\}}|j|||}}}|jDcic] \}}||vs ||}}}t|dk(r|S|Dcgc]}||jvs|} }t|}tj|||} | | _| Scc}wcc}wcc}}wcc}}wcc}w)Nz%s is not a tensor expressionr)rrrrrrrwrrr}rLrrrr) rrrKr expr_free_indicesrVname_translationrhr3rrs r4rzTensorElement.__new__sK$'dH- ?$ FGG499 R}S)<RS S 1132CDQAFF1IqLDDS\SbSbSde<5%%))%7>e e6?oo6GfleU5TeKeUE\f f y>Q K#4RaAQ8QR RO sD)4( SDefSs* E4E  E E E<EEcjt|jDcgc] \}}||f c}}Scc}}wrY)rar)rOrVrhs r4rHzTensorElement.frees,+4T5J5J5L+MNxq% NNNs/cgSrYr2rns r4rIzTensorElement.dums  r3c |jdSrB_argsrns r4rzTensorElement.exprzz!}r3c |jdSrr~rns r4rKzTensorElement.index_maprr3c"tjSrYrrns r4r zTensorElement.coeffrr3c|SrYr2rns r4r_zTensorElement.nocoeffrr3c|jSrYrrns r4rzTensorElement.get_free_indicess!!!r3c$|j|SrYrxrfs r4rhzTensorElement._replace_indicess}}T""r3c"|jSrY)rrns r4rozTensorElement.get_indicess$$&&r3c|jj|\}}|jtfd|D}|Dcgc] }|vs| }}|j |}||fScc}w)Nc3TK|]}j|td!ywrY)r}slice)rrVrKs r4rz.TensorElement._extract_data..s OaIMM!U4[9Os%()rrrKrr)rOrrr slice_tuplerVrKs @r4rzTensorElement._extract_datasq!YY445EF UNN O;OO "-DQ)1CqD D!!+.E!!Es  A)A)Nr)rrrrrrrHrIrrKr r_rrhrorr2r3r4rwrws6$OO"#'"r3rwc.eZdZdZddZedZdZy)WildTensorHeada A wild object that is used to create ``WildTensor`` instances Explanation =========== Examples ======== >>> from sympy.tensor.tensor import TensorHead, TensorIndex, WildTensorHead, TensorIndexType >>> R3 = TensorIndexType('R3', dim=3) >>> p = TensorIndex('p', R3) >>> q = TensorIndex('q', R3) A WildTensorHead can be created without specifying a ``TensorIndexType`` >>> W = WildTensorHead("W") Calling it with a ``TensorIndex`` creates a ``WildTensor`` instance. >>> type(W(p)) The ``TensorIndexType`` is automatically detected from the index that is passed >>> W(p).component W(R3) Calling it with no indices returns an object that can match tensors with any number of indices. >>> K = TensorHead('K', [R3]) >>> Q = TensorHead('Q', [R3, R3]) >>> W().matches(K(p)) {W: K(p)} >>> W().matches(Q(p,q)) {W: Q(p, q)} If you want to ignore the order of indices while matching, pass ``unordered_indices=True``. >>> U = WildTensorHead("U", unordered_indices=True) >>> W(p,q).matches(Q(q,p)) >>> U(p,q).matches(Q(q,p)) {U(R3,R3): _WildTensExpr(Q(q, p))} Parameters ========== name : name of the tensor unordered_indices : whether the order of the indices matters for matching (default: False) See also ======== ``WildTensor`` ``TensorHead`` Nc t|tr t|}nt|tr|}n td|g}|tj t |}n|jt |k(sJ|tj t |k7r tdtj||t|t|t|t|}|S)Nrz3Wild matching based on symmetry is not implemented.) rr~rrdrrrLrrrrrr)rrbrJrrunordered_indicesrrs r4rzWildTensorHead.__new__s dC  ,K f %K^, ,  K  %11#k2BCH==C $44 44 ~11#k2BC C%&[\ \mmCe[.A78CTV]^bVcelm~eA r3c |jdS)Nr,rrns r4rz WildTensorHead.unordered_indices1rr3c<t||fi|}|jSrY)rr$)rOrKrrs r4razWildTensorHead.__call__5sD'4V4{{}r3)NNrF)rrrrrrrrar2r3r4rrs&6n.r3rc&eZdZdZdZddZddZy)ra A wild object which matches ``Tensor`` instances Explanation =========== This is instantiated by attaching indices to a ``WildTensorHead`` instance. Examples ======== >>> from sympy.tensor.tensor import TensorHead, TensorIndex, WildTensorHead, TensorIndexType >>> W = WildTensorHead("W") >>> R3 = TensorIndexType('R3', dim=3) >>> p = TensorIndex('p', R3) >>> q = TensorIndex('q', R3) >>> K = TensorHead('K', [R3]) >>> Q = TensorHead('Q', [R3, R3]) Matching also takes the indices into account >>> W(p).matches(K(p)) {W(R3): _WildTensExpr(K(p))} >>> W(p).matches(K(q)) >>> W(p).matches(K(-p)) If you want to match objects with any number of indices, just use a ``WildTensor`` with no indices. >>> W().matches(K(p)) {W: K(p)} >>> W().matches(Q(p,q)) {W: Q(p, q)} See Also ======== ``WildTensorHead`` ``Tensor`` c |jdd}|jtk(r t||fd|i|S|jtk(r||S|j ||}|Dcgc]}|j }}|j|j|d|j|j}tj||t|}|j|_tj||_|jj dd|_|jj$dd|_|jj(|_t*j,|_||_||_|g|_|j6t9|k7r t;d||_|j?||j|_ |Scc}w)NrF)rrrr-)!r!rrr _WildTensExprr.rTrbrrrrrr>rWrrHr/rIr0rMrrVr1r2r3r4rrLrdrr5r6)rr7rKrrrrJrs r4rzWildTensor.__new__^skk-7   z ) +wSKS7S S    .( ($$[':8?@s,,@ @!&&    !!);; ' mmCeWo>##.;;WE((--a0 ''++A.,,66 UU  $&-   s7| +67 7%--gs7K7KL 1As$GNcpt|ts|tdk7ry|i}n|j}t |j dkDrt |dsy|j}t |t |j k7ry|jjr&|j|}|y|j|nOtt |D]8}|j |j||}|y|j|:t|||j<|S|||<|S)Nr`rr)rrrrRrLrKrbrr3r_match_indices_ignoring_orderrzrr rr])rOrrrS expr_indicesr@rVs r4r zWildTensor.matchess"$)dadl  I!(I t|| q 4!34002L< C $550066t<9$$Q's<01,A Q// Q@Ay#!((+ ,)6d(;Idnn % #IdOr3c|i}n|j}d}t|j|}g}|j}|dD]M}d} |D]@} | |vr|j | } | d} |j | |j | n| rMy|D cgc] } | |vs|  }} |dD]k}d} |D]^} |j | } | | |jvr||  | |k7ryd} |j | |j | n| rky|D cgc] } | |vs|  }} |dD]k}d} |D]^} |j | } | | |jvr||  | |k7ryd} |j | |j | n| rkyt|t|jkry|Scc} wcc} w)z~ Helper method for matches. Checks if the indices of self and expr match disregarding index ordering. Nc@t|tr|jryyy)N wild, updownwildr)rWildTensorIndex ignore_updown)rs r4rz9WildTensor._match_indices_ignoring_order..siftkeys#/$$)! r3rFTrr) rRr*rKror rzrerrL) rOrrrSrindices_siftedmatched_indicesexpr_indices_remainingrmatched_this_inde_indr@rVs r4rz(WildTensor._match_indices_ignoring_ordersH  I!(I !dllG4!%!1!1!3!), C$ / O+KK&='+$$$Q'#**51 $ .D!`qP_G_!!`!`!&) C$ / KK&=ty~~//Yt_4D#4N#'+$$$Q'#**51 $ .D!`qP_G_!!`!`!.1 C$ / KK&=ty~~//Yt_4D#4N#'+$$$Q'#**51 $   #dll"3 3 C"a"as G$G! G +G r)rrrrrr rr2r3r4rr:s"F$N"HFr3rc6eZdZdZddZedZdZddZy) ra A wild object that matches TensorIndex instances. Examples ======== >>> from sympy.tensor.tensor import TensorIndex, TensorIndexType, WildTensorIndex >>> R3 = TensorIndexType('R3', dim=3) >>> p = TensorIndex("p", R3) By default, covariant indices only match with covariant indices (and similarly for contravariant) >>> q = WildTensorIndex("q", R3) >>> (q).matches(p) {q: p} >>> (q).matches(-p) If you want matching to ignore whether the index is co/contra-variant, set ignore_updown=True >>> r = WildTensorIndex("r", R3, ignore_updown=True) >>> (r).matches(-p) {r: -p} >>> (r).matches(p) {r: p} Parameters ========== name : name of the index (string), or ``True`` if you want it to be automatically assigned tensor_index_type : ``TensorIndexType`` of the index is_up : flag for contravariant index (is_up=True by default) ignore_updown : bool, Whether this should match both co- and contra-variant indices (default:False) crt|tr t|}nmt|tr|}nZ|durKdjt |j }t|}|j j |n tdt|}t|}tj|||||Srr)rrbrTrcrrs r4rzWildTensorIndex.__new__s dC  ,K f %K T\==%6%E%E!FGD ,K  , , 3 3K @^, , . }}S+/@%WWr3c |jdSrrrns r4rzWildTensorIndex.ignore_updown%rr3ctt|j|j|j |j}|SrY)rrbrTrcrrs r4rzWildTensorIndex.__neg__)s0 TYY(>(>ZZ$"4"46 r3Nct|tsy|j|jk7ry|js|j|jk7ry|i}n|j }|||<|SrY)rrrrTrrcrRrs r4r zWildTensorIndex.matches.sh$ ,  ! !T%;%; ;!!zzTZZ'  I!(I $r3)TFr) rrrrrrrrr r2r3r4rrs,"FX  r3rcdeZdZdZdZdZdZdZdZdZ dZ d Z d Z d Z d Zd ZdZdZy)ra INTERNAL USE ONLY This is an object that helps with replacement of WildTensors in expressions. When this object is set as the tensor_head of a WildTensor, it replaces the WildTensor by a TensExpr (passed when initializing this object). Examples ======== >>> from sympy.tensor.tensor import WildTensorHead, TensorIndex, TensorHead, TensorIndexType >>> W = WildTensorHead("W") >>> R3 = TensorIndexType('R3', dim=3) >>> p = TensorIndex('p', R3) >>> q = TensorIndex('q', R3) >>> K = TensorHead('K', [R3]) >>> print( ( K(p) ).replace( W(p), W(q)*W(-q)*W(p) ) ) K(R_0)*K(-R_0)*K(p) cHt|ts td||_y)Nz,_WildTensExpr expects a TensExpr as argument)rrrr)rOrs r4rQz_WildTensExpr.__init__Ts$)JK K r3c|jjtt|jj |SrY)rrhrrr)rOrKs r4raz_WildTensExpr.__call__Ys1yy))$s4993M3M3OQX/Y*Z[[r3cZ|j|jtjzSrY)rrrr=rns r4rz_WildTensExpr.__neg__\syy1==011r3ctrYr?rns r4r@z_WildTensExpr.__abs___rAr3c|j|jk7r%td|jd|j|j|j|jzSNz Cannot add z to rrrrs r4rGz_WildTensExpr.__add__bsN :: "k$))D EF Fyy5::-..r3c|j|jk7r%td|jd|j|j|j|jzSrrrs r4rIz_WildTensExpr.__radd__gsN :: "k$))D EF FyyDII-..r3c|| zSrYr2rs r4rLz_WildTensExpr.__sub__lsvr3c|| zSrYr2rs r4rNz_WildTensExpr.__rsub__osr3ctrYr?rs r4rQz_WildTensExpr.__mul__rrAr3ctrYr?rs r4rSz_WildTensExpr.__rmul__urAr3ctrYr?rs r4rWz_WildTensExpr.__truediv__xrAr3ctrYr?rs r4rYz_WildTensExpr.__rtruediv__{rAr3ctrYr?rs r4r+z_WildTensExpr.__pow__~rAr3ctrYr?rs r4r\z_WildTensExpr.__rpow__rAr3N)rrrrrQrarr@rGrIrLrNrQrSrWrYr+r\r2r3r4rr@sN& \2"/ / """"""r3rcFt|tr|jS|S)zt Butler-Portugal canonicalization. See ``tensor_can.py`` from the combinatorics module for the details. )rrrP)rs r4rPrPs !Xzz| Hr3c~|s&tjtjgggS|d}|ddD]}||z} |S)z product of tensors rr`N)rrrrV)rrtxs r4 tensor_mulrsN   B33 !Ae bD Hr3cDt|jd}|Dcgc]}|d c}\}}}}|tddz}|j||f||f||f||f tddz}|j||f||f||f||ftddz} ||z| z} | Scc}w)z replace Riemann tensor with an equivalent expression ``R(m,n,p,q) -> 2/3*R(m,n,p,q) - 1/3*R(m,q,n,p) + 1/3*R(m,p,n,q)`` c |dSrr2rCs r4rEz(riemann_cyclic_replace..s !A$r3rFrr@rjr`)rrHr rj) t_rrHrDr@rfrqt0rt2t3s r4riemann_cyclic_replacers #(( /D $%1!A$%JAq!Q Xa^ B !A!uaUAa5 9 9(1a. HB  1qeQqE1Q% 8!Q GB b2B I &s Bc |j}t|ttfr|g}n |j}|Dcgc]}|j }}|Dcgc]}|Dcgc] }t |c}}}}|Dcgc] }t| }}t|jd} | s| St| Scc}wcc}wcc}}wcc}w)aJ Replace each Riemann tensor with an equivalent expression satisfying the cyclic identity. This trick is discussed in the reference guide to Cadabra. Examples ======== >>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, riemann_cyclic, TensorSymmetry >>> Lorentz = TensorIndexType('Lorentz', dummy_name='L') >>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz) >>> R = TensorHead('R', [Lorentz]*4, TensorSymmetry.riemann()) >>> t = R(i,j,k,l)*(R(-i,-j,-k,-l) - 2*R(-i,-k,-j,-l)) >>> riemann_cyclic(t) 0 FrD) rrrrrrrrrr$rP) rrrDa1rra2ra3rs r4riemann_cyclicrs$ B"w'(tww! "!'') "B "<> ?q 2" !" % 2 ?B ?"$ %Q*a. %B % "    &B  | # 2 ? %s#B8 C"B= 4CC=Ccd}|j}i}|jD]}t|ts||vr|j}g}t t |D]}|||us |j |t |dkDrtd|zt |dk(s|||<g} g} g} |jD]L\} } }}|||vr||k(r| j |g)|||}|||}| |vr>|j| |j| k(rt|||}| |dk(r||fn||f\}}| j}| D]}|d|k(ra|d|k(rF|jt|}| j || j ||d|d|zn|j |n1|d|k(su|jd|n|j ||g|Dcgc] }|| vs| } }|| } Og}| D]}|D]}|j || D]}|D]}|j |t t |Dcgc] }||vs| }}| | |fScc}wcc}w)z Returns ``(lines, traces, rest)`` for an index type, where ``lines`` is the list of list of positions of a matrix line, ``traces`` is the list of list of traced matrix lines, ``rest`` is the rest of the elements of the tensor. cd}|t|krW||}|d}|d}d}|r.d}t|dzt|D] }|t|k\rn||d|k(r0d}|j||dd|d}|j|O||d|k(r8d}t ||dd|z||<||}||d}|j|||d|k(r9d}|jt ||dd|d}|j|||d|k(sd}||dd|z||<||}|d}|j||r.|dz }|t|krW|S)NrrTFr`)rLrrZr!reversed)rrVrDxendxstarthitrs r4 _join_lineszget_lines.._join_liness #a&j!AR5DqTFCq1uc!f-!ACF{tAw$"1ab* ua tAw&("'!QR1A5!aD!"1aa tBx4'"!A$s)!45 ua tBx6)" tCRy1}!aD!"1a 9!> FAI#a&jJr3r@z&at most two indices of type %s allowedr`rrN)rrrrJrrLrerdrgrhrrRmininsert)exrr argumentsdtrvrJrrVlinestracestraces1r2rec0c1ta0ta1b0b1lines1linerfrDrestrs r4 get_linesrs'RI B WW !X&  7 mm s;'( A1~+   q6A:E RS S q6Q;BqE  E FG..%#BB R= "  8 MM2$  22 S=  99R=CIIbM ) & %23q6\"bRB $DBx2~7b= 3t9-ANN4(MM$qr(T"1X"56KKOaB Ar" $ MM2r( #"7qaw&677E"K%#L D A KKN  A KKN S^, >! A >D > &$ 8 ?s0 I6:I6! I;+I;cDt|tsy|jSNr2)rrrrs r4rrCs a "   r3cDt|tsy|jSr)rrrors r4roroIs a " ==?r3ct|tsy|j}|j}|Dcgc] }||vs| c}Scc}wr)rrror)rrrHrVs r4rrNsB a " ==?D   D -!q}A -- -s AAcVt|tr |jStggggSrY)rrrr>rs r4rrUs)!X!!! 2r2r **r3ct|trtjSt|tr |j St|t r td|S)Nz3no coefficient associated to this tensor expression)rrrrVrr rrdrs r4 get_coeffr[sC!Vuu !Www!XNOO Hr3cHt|tr|j|S|SrY)rrr)rrs r4rrds"!X  ## Hr3ct|ts|St|ttfrHt |j ||}|j ||}|dt|dz k7r| S|St)z Returns the tensor corresponding to the permutation ``g`` For further details, see the method in ``TIDS`` with the same name. r+rr`) rrrrrrrVrLr)rrrnimrs r4rrisz a " A( )!!$00 0L((+(F R5CFQJ 4K  r3cDt|ts|S|j|SrY)rrrj)rrks r4rjrj{s$ a " 1   ..r3c ft|jtttt t SrY)ratomsrrrrr)rs r4rrs  4z?N[ \\r3cfd}|S)Nctttti}t d|j Dr||j S|S)Nc3<K|]}t|tywrY)rr)rrs r4rz._postprocessor..s:1z!X&:s)rrrrrr)r tens_classrs r4_postprocessorz)get_postprocessor.._postprocessors<7C1#6 : : :tyy) )Kr3r2)rrs` r4get_postprocessorrs r3rrBr)kr __future__rtypingr functoolsrmathrabcrr collectionsr r=r\sympy.core.numbersr r rr sympy.combinatorics.tensor_canrrrr sympy.corerrrrrrsympy.core.cachersympy.core.containersrrsympy.core.functionrsympy.core.sortingrsympy.core.symbolrrrsympy.core.sympifyr r!sympy.core.operationsr"sympy.external.gmpyr#sympy.matricesr$sympy.utilities.exceptionsr%r&r'sympy.utilities.decoratorr(r)sympy.utilities.iterablesr*r5r9r<r>rrrrerr%rrrrrrrrr:rrrrrwrrrrrPrrrrrrorrrrrrjrr"_constructor_postprocessor_mappingr2r3r4r s>###2+4488(-,/334)*99C*   h)kh)V  &s{sj ":!;jjZ k6ek6\ Z%Zz:A9UA9H #: ?,?,B ;"6 ;_;_  ;_| #6 ,,0O5O5d ntSnb ihiX aKXaKJDchDcL"W"HW"tUZUpuunLkL^B"EB"J    Bup  .+    $/ ] c " #6((2r3