K iJ<dZddlmZddlmZddlmZmZddlm Z m Z ddl m Z ddl mZGdd eZd Zd Zdd Zd ZdZdZdZy)aAModule with functions operating on IndexedBase, Indexed and Idx objects - Check shape conformance - Determine indices in resulting expression etc. Methods in this module could be implemented by calling methods on Expr objects instead. When things stabilize this could be a useful refactoring. )reduce)Function)exp Piecewise)IdxIndexed)sift) OrderedDictc eZdZy)IndexConformanceExceptionN)__name__ __module__ __qualname__`/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/sympy/tensor/index_methods.pyr r srr cft|D]}|vrd|< d|<tfddS)aJ Returns the unique and repeated indices. Also note, from the examples given below that the order of indices is maintained as given in the input. Examples ======== >>> from sympy.tensor.index_methods import _unique_and_repeated >>> _unique_and_repeated([2, 3, 1, 3, 0, 4, 0]) ([2, 1, 4], [3, 0]) rc|SNr)xuniqs rz&_unique_and_repeated..+s QrT)binary)r r )indsirs @r_unique_and_repeatedrsF =D  9DGDG  ' 55rcJt|\}}t|t|fS)a0 Removes repeated objects from sequences Returns a set of the unique objects and a tuple of all that have been removed. Examples ======== >>> from sympy.tensor.index_methods import _remove_repeated >>> l1 = [1, 2, 3, 2] >>> _remove_repeated(l1) ({1, 3}, (2,)) )rsettuple)rurs r_remove_repeatedr#-s%  %DAq q658 rcZttt|j}tt |\}}ttt|}tt d|}t |\}}i}|D]&}|D]}||vr||xx||zcc<||||<!(|r|||fS||fS)aDetermine the outer indices of a Mul object. Examples ======== >>> from sympy.tensor.index_methods import _get_indices_Mul >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Mul(x[i, k]*y[j, k]) ({i, j}, {}) >>> _get_indices_Mul(x[i, k]*y[j, k], return_dummies=True) ({i, j}, {}, (k,)) c ||zSrrrys rrz"_get_indices_Mul..Ws AEr)listmap get_indicesargsziprr#)exprreturn_dummiesrsymsdummiessymmetryspairs r_get_indices_Mulr4As$ K+ ,Dc4j!JD$ D$ D )40 1D$T*MD'H ) )Dx!D')!"4  ))Xw&&X~rcv|j\}}t|\}}t|\}}||z}i}||fS)aDetermine outer indices of a power or an exponential. A power is considered a universal function, so that the indices of a Pow is just the collection of indices present in the expression. This may be viewed as a bit inconsistent in the special case: x[i]**2 = x[i]*x[i] (1) The above expression could have been interpreted as the contraction of x[i] with itself, but we choose instead to interpret it as a function lambda y: y**2 applied to each element of x (a universal function in numpy terms). In order to allow an interpretation of (1) as a contraction, we need contravariant and covariant Idx subclasses. (FIXME: this is not yet implemented) Expressions in the base or exponent are subject to contraction as usual, but an index that is present in the exponent, will not be considered contractable with its own base. Note however, that indices in the same exponent can be contracted with each other. Examples ======== >>> from sympy.tensor.index_methods import _get_indices_Pow >>> from sympy import Pow, exp, IndexedBase, Idx >>> A = IndexedBase('A') >>> x = IndexedBase('x') >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> _get_indices_Pow(exp(A[i, j]*x[j])) ({i}, {}) >>> _get_indices_Pow(Pow(x[i], x[i])) ({i}, {}) >>> _get_indices_Pow(Pow(A[i, j]*x[j], x[i])) ({i}, {}) ) as_base_expr*) r-baserbindsbsymseindsesymsr symmetriess r_get_indices_Powr=hsNP  "ID#t$LE5s#LE5 5=DJ  rcVttt|j}tt |\}}|Dcgc]}|t k7s|c}s t ifSt fdddDstd|ztd|s|d}ni}d|fScc}w)abDetermine outer indices of an Add object. In a sum, each term must have the same set of outer indices. A valid expression could be x(i)*y(j) - x(j)*y(i) But we do not allow expressions like: x(i)*y(j) - z(j)*z(j) FIXME: Add support for Numpy broadcasting Examples ======== >>> from sympy.tensor.index_methods import _get_indices_Add >>> from sympy.tensor.indexed import IndexedBase, Idx >>> i, j, k = map(Idx, ['i', 'j', 'k']) >>> x = IndexedBase('x') >>> y = IndexedBase('y') >>> _get_indices_Add(x[i] + x[k]*y[i, k]) ({i}, {}) c3.K|] }|dk(yw)rNr).0r non_scalarss r z#_get_indices_Add..s.sqAv{rr) r(r)r*r+r,rallr r)r-rr/rr<rAs @r_get_indices_AddrEs6 K+ ,Dc4j!JD$#1a35j11K uby : %%2s B&B&ct|tr|j}t|\}}|ifS| t ifSt|t r|hifS|j r t ifS|jr t|S|jr t|S|jst|tr t|St|tr t ifSt|tr2t }|j D]}t#|\}}||z}|fS|j%ts t ifSt'dt)|z)aDetermine the outer indices of expression ``expr`` By *outer* we mean indices that are not summation indices. Returns a set and a dict. The set contains outer indices and the dict contains information about index symmetries. Examples ======== >>> from sympy.tensor.index_methods import get_indices >>> from sympy import symbols >>> from sympy.tensor import IndexedBase >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, a, z = symbols('i j a z', integer=True) The indices of the total expression is determined, Repeated indices imply a summation, for instance the trace of a matrix A: >>> get_indices(A[i, i]) (set(), {}) In the case of many terms, the terms are required to have identical outer indices. Else an IndexConformanceException is raised. >>> get_indices(x[i] + A[i, j]*y[j]) ({i}, {}) :Exceptions: An IndexConformanceException means that the terms ar not compatible, e.g. >>> get_indices(x[i] + y[j]) #doctest: +SKIP (...) IndexConformanceException: Indices are not consistent: x(i) + y(j) .. warning:: The concept of *outer* indices applies recursively, starting on the deepest level. This implies that dummies inside parenthesis are assumed to be summed first, so that the following expression is handled gracefully: >>> get_indices((x[i] + A[i, j]*y[j])*x[j]) ({i, j}, {}) This is correct and may appear convenient, but you need to be careful with this as SymPy will happily .expand() the product, if requested. The resulting expression would mix the outer ``j`` with the dummies inside the parenthesis, which makes it a different expression. To be on the safe side, it is best to avoid such ambiguities by using unique indices for all contractions that should be held separate. )FIXME: No specialized handling of type %s) isinstancerindicesr#rris_Atomis_Mulr4is_AddrEis_Powrr=rrr+r*hasNotImplementedErrortype)r-crr0ind0argindsyms rr*r*s>n$ LL(+ gRx uby D# vrz uby ;;#D) ) [[#D) ) [[JtS1#D) ) i (5"9  h '5Dyy &s+S  9 '"5"9 ! 7$t* DF FrcVt|tr!t|j\}}|xsd|hiS|jrd|hiS|j ret |d\}}}|xsd|hi}g}|jD]1}t|}d|vrt|dk(r!|j|3|r|||<|S|jst|trf|j\}}t|} t|} g} | | fD]&} d| vrt| dk(r| j| (d|hi}| r| ||<|S|jrDi}|jD]1} t| } | D]}||vr||xx| |zcc<| |||<!3|St|trd|iSt|t rPg}|jD]1}t|}d|vrt|dk(r!|j|3d|hi} |r|| |<| S|j#tsd|hiSt%dt'|z)aDetermine dummy indices of ``expr`` and describe its structure By *dummy* we mean indices that are summation indices. The structure of the expression is determined and described as follows: 1) A conforming summation of Indexed objects is described with a dict where the keys are summation indices and the corresponding values are sets containing all terms for which the summation applies. All Add objects in the SymPy expression tree are described like this. 2) For all nodes in the SymPy expression tree that are *not* of type Add, the following applies: If a node discovers contractions in one of its arguments, the node itself will be stored as a key in the dict. For that key, the corresponding value is a list of dicts, each of which is the result of a recursive call to get_contraction_structure(). The list contains only dicts for the non-trivial deeper contractions, omitting dicts with None as the one and only key. .. Note:: The presence of expressions among the dictionary keys indicates multiple levels of index contractions. A nested dict displays nested contractions and may itself contain dicts from a deeper level. In practical calculations the summation in the deepest nested level must be calculated first so that the outer expression can access the resulting indexed object. Examples ======== >>> from sympy.tensor.index_methods import get_contraction_structure >>> from sympy import default_sort_key >>> from sympy.tensor import IndexedBase, Idx >>> x, y, A = map(IndexedBase, ['x', 'y', 'A']) >>> i, j, k, l = map(Idx, ['i', 'j', 'k', 'l']) >>> get_contraction_structure(x[i]*y[i] + A[j, j]) {(i,): {x[i]*y[i]}, (j,): {A[j, j]}} >>> get_contraction_structure(x[i]*y[j]) {None: {x[i]*y[j]}} A multiplication of contracted factors results in nested dicts representing the internal contractions. >>> d = get_contraction_structure(x[i, i]*y[j, j]) >>> sorted(d.keys(), key=default_sort_key) [None, x[i, i]*y[j, j]] In this case, the product has no contractions: >>> d[None] {x[i, i]*y[j, j]} Factors are contracted "first": >>> sorted(d[x[i, i]*y[j, j]], key=default_sort_key) [{(i,): {x[i, i]}}, {(j,): {y[j, j]}}] A parenthesized Add object is also returned as a nested dictionary. The term containing the parenthesis is a Mul with a contraction among the arguments, so it will be found as a key in the result. It stores the dictionary resulting from a recursive call on the Add expression. >>> d = get_contraction_structure(x[i]*(y[i] + A[i, j]*x[j])) >>> sorted(d.keys(), key=default_sort_key) [(A[i, j]*x[j] + y[i])*x[i], (i,)] >>> d[(i,)] {(A[i, j]*x[j] + y[i])*x[i]} >>> d[x[i]*(A[i, j]*x[j] + y[i])] [{None: {y[i]}, (j,): {A[i, j]*x[j]}}] Powers with contractions in either base or exponent will also be found as keys in the dictionary, mapping to a list of results from recursive calls: >>> d = get_contraction_structure(A[j, j]**A[i, i]) >>> d[None] {A[j, j]**A[i, i]} >>> nested_contractions = d[A[j, j]**A[i, i]] >>> nested_contractions[0] {(j,): {A[j, j]}} >>> nested_contractions[1] {(i,): {A[i, i]}} The description of the contraction structure may appear complicated when represented with a string in the above examples, but it is easy to iterate over: >>> from sympy import Expr >>> for key in d: ... if isinstance(key, Expr): ... continue ... for term in d[key]: ... if term in d: ... # treat deepest contraction first ... pass ... # treat outermost contactions here NT)r.rrG)rHrr#rIrJrKr4r+get_contraction_structurelenappendrMrr6rLrrrNrOrP)r-junkkeyresultnestedfacfacdbedbasedexpdictsdtermdeeplistrSdeeps rrWrW*scL$ $T\\2 c tdV$$ tf~ *4EdC+v&99 $C,S1DDLSY!^ d# $ !F4L  4-!1)!,(+ AAI#a&A+ Q   F4L  II )D)$/A )&=3K1S6)K"#C&F3K  ) ) D) $d| D( #99 &C,S1DDLSY!^% &D6N AdGXXg tf~ 3d4j@ BBrN)F)__doc__ functoolsrsympy.core.functionrsympy.functionsrrsympy.tensor.indexedrrsympy.utilitiesr collectionsr Exceptionr rr#r4r=rEr*rWrrrrqsU (*- #  6(($N1h+&\]F@kBr